OS-04 Threads & Concurrency

Review

A thread is a basic unit of CPU utilization.

review

Thread Concept

对比维度进程线程
单元性质独立执行单元,能独立执行任务,拥有独立资源集合进程的子集,不能脱离进程单独存在,一个进程可含多个线程
状态信息保存大量状态信息,如进程ID、状态、内存管理信息、文件描述符列表等共享进程状态、内存和其他资源,仅维护线程ID、程序计数器、寄存器集合、栈空间等少量信息
地址空间拥有独立的地址空间,不同进程地址空间相互隔离共享所属进程的地址空间
通信方式依赖进程间通信(IPC)机制,如管道、消息队列、共享内存、信号量等除IPC机制外,还可通过共享内存直接通信
上下文切换切换时需保存和恢复全部状态信息,涉及大量数据操作,开销大,速度相对较慢仅需保存和恢复少量状态信息,且因共享地址空间无需切换内存管理信息,速度更快

multithreaded process

pros:

  • 多相似任务处理
  • 利用多核系统
  • 创建线程轻量化

examples:

  • Client-server applications
  • Most operating system kernels are multithreaded

Multicore Programming

对比维度Concurrency(并发)Parallelism(并行)
核心定义逻辑上“同时”处理多个任务,通过任务切换实现物理上“同时”执行多个任务,依赖多核或分布式系统
实现方式时间片轮询(如操作系统线程调度)
多线程、异步 I/O、事件循环,Web 服务器处理多个请求(如 Nginx)
直接分配独立资源(如多核 CPU 或分布式节点)
多线程、多进程、GPU/TPU 加速、分布式计算,科学计算(如天气模拟)、图像处理(如 GPU 渲染)
资源需求低,单核 CPU 即可高,需多核 CPU 或分布式系统
适用任务类型I/O 密集型(如数据库查询、网络请求)计算密集型(如矩阵运算、AI 训练)
性能瓶颈上下文切换开销、I/O 延迟任务可拆分性、通信开销(分布式场景)
编程复杂度高,需处理异步逻辑、回调地狱极高,需处理同步、分布式一致性
实际应用浏览器同时渲染多个标签页
聊天服务器处理大量用户消息
视频渲染软件利用 GPU 加速
搜索引擎在集群中并行爬取网页

Parallelism

  • Data Parallelism
    • Mapreduce
    • Distributed Machine Learning
  • Task Parallelism
    • Federated Submodel Learning
    • Distributed Machine Learning

Amdahl’s Law

公式:

  • P: 串行部分的比例
  • N: 并行部分的数量

取决于串行部分的比例

Multithreading Models

对比维度Kernel Threads(内核线程)User Threads(用户线程)
核心定义由操作系统内核直接管理和调度,内核可见,直接分配CPU时间片和资源,独立执行由用户空间的线程库管理,内核不可见,被视为单个进程内的轻量级任务,依赖内核线程执行
管理主体操作系统内核用户空间的线程库(如Pthreads、Java Threads)
执行依赖独立执行,内核直接调度依赖内核线程执行(需映射到内核线程)
调度权内核完全控制调度策略(如时间片轮转)用户线程库自行调度(如协作式或抢占式)
上下文切换开销高(需内核态与用户态切换)低(仅在用户空间切换)
多核利用支持真正并行(每个内核线程独立运行)受限于内核线程数量(需映射到多核)
资源占用每个线程占用独立内核栈(通常较大)共享进程内核栈,用户栈较小(轻量级)
阻塞影响单个线程阻塞不影响其他线程若映射到同一内核线程,阻塞会导致所有用户线程暂停
适用场景计算密集型任务、实时系统I/O密集型任务、高并发场景
典型实现Windows、Linux、macOS等操作系统原生支持Pthreads(POSIX)、Java线程(早期版本)、Go语言的Goroutine(混合模型)
映射模型一对一(1:1),每个内核线程独立执行多对一(M:1):多个用户线程映射到一个内核线程(如早期Java线程);多对多(M:N):用户线程动态映射到内核线程(如Linux的NPTL、现代Java线程)
优点支持多核并行、健壮性强、无阻塞风险轻量级、高并发能力、上下文切换快
缺点上下文切换开销大、资源占用高多核利用率低、阻塞易导致性能下降
通俗比喻如同工厂中的正式员工,由厂长(内核)直接分配任务,每人独立工作,但请假(阻塞)不影响其他人如同工厂中的外包团队,由包工头(线程库)管理,共享一套设备(内核线程),若一人请假(阻塞),整个团队可能暂停
实际应用示例科学计算(如矩阵运算)、视频渲染(利用GPU多核)Web服务器处理大量并发请求(如Node.js的事件循环)、游戏中的多任务处理(如动画与网络通信)

Thread models

Lightweight process (LWP): A mapping between user threads and kernel threads

Multithreading models

对比维度多对一(M:1)一对一(1:1)多对多(M:N)
映射关系多个用户线程映射到一个内核线程每个用户线程直接映射到一个内核线程用户线程动态映射到多个内核线程
内核感知内核不可见,仅用户线程库管理内核直接管理每个线程内核可见,用户线程与内核线程动态关联
上下文切换开销低(仅在用户空间切换)高(需内核态与用户态切换)中等(结合两者特性)
多核利用率无法利用多核(受限于单个内核线程)充分利用多核(每个线程独立运行)高效利用多核(动态分配内核线程)
资源占用低(共享内核栈)高(每个线程独立内核栈)中等(按需分配内核线程)
阻塞影响若内核线程阻塞,所有用户线程暂停单个线程阻塞不影响其他线程部分线程阻塞时,其他线程仍可运行
适用场景I/O密集型任务(如早期Java线程)计算密集型任务(如科学计算)高并发场景(如Web服务器、Go语言Goroutine)
典型实现早期Java线程(Solaris绿色线程)、旧版LWP(轻量级进程)Linux内核线程(clone系统调用)、Windows线程GNU的NPTL(Linux原生线程库)、Java 1.2+、Go语言Goroutine
优点轻量级、上下文切换快支持多核并行、无阻塞风险高效利用多核、高并发能力强
缺点无法利用多核,阻塞风险高资源占用高、上下文切换开销大实现复杂度高,需动态调度
通俗比喻一个厨师(内核线程)同时处理多个订单(用户线程),但一次只能做一道菜(阻塞时全部暂停)每个订单(用户线程)由独立厨师(内核线程)处理,效率高但需雇佣更多厨师(资源消耗大)根据订单量(用户线程)动态分配厨师(内核线程),高峰期多派厨师,空闲时减少(平衡资源与效率)
实际应用场景早期Java Web服务器(轻量级并发)视频渲染、科学计算(需多核并行)现代Web服务器(如Node.js、Go)、分布式系统(如Kafka)

Thread Libraries

Tools for manage threads

  • POSIX Pthreads
  • Java Threads
  • Win32 Threads

Pthreads

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <pthread.h> 
#include <stdio.h>
#include <stdlib.h>
int sum; /* this data is shared by the thread(s) */
void *runner(void *param); /* threads call this function */
int main(int argc, char *argv[])
{
pthread_t tid; /* create thread identifier */
pthread_attr_t attr; /* create thread attributes */
/* set the default attributes of the thread */
pthread_attr_init(&attr);
/* create the thread */
pthread_create(&tid, &attr, runner, argv[1]);
/* wait for the thread to exit */
pthread_join(tid, NULL);
printf("sum = %d ∖ n",sum);

Implicit Threading

  • Thread Pools
    • 提前创建多个线程,等待任务到来
  • Fork-Join
    • 分治法
  • OpenMP
    • 并行编程接口
  • Grand Central Dispatch
    • 苹果的并行编程框架
  • Intel Threading Building Blocks
    • 英特尔的并行编程框架

Threading Issues

Threading Issues

关于Kernel和User

学习操作系统到这里,我发现大量出现Kernel和User的概念,这里有一些自己的探索与思考。

溯源

  • Multics系统(1960年代)
    由MIT、贝尔实验室和通用电气联合开发的分时操作系统,首次提出了层次化保护机制(Protection Rings)。系统分为多个特权级别(如Ring 0到Ring 3),其中:

    • 内核态(Kernel Mode):运行在最高特权级(如Ring 0),可访问所有硬件资源和系统数据。
    • 用户态(User Mode):运行在较低特权级(如Ring 3),受限访问敏感资源。
  • UNIX的继承与简化
    贝尔实验室的Ken Thompson和Dennis Ritchie在设计UNIX时,受Multics启发,但简化了保护机制,仅保留两级模式

    • 内核态:执行内核代码,直接操作硬件。
    • 用户态:运行应用程序,通过系统调用请求内核服务。

为什么需要用户态与内核态分离?

  • 安全性:防止用户程序恶意或误操作破坏内核。
  • 稳定性:用户程序崩溃不会影响内核运行。
  • 资源管理:内核统一调度硬件资源(如CPU、内存)。

思考

内核态与用户态的设计哲学是典型的抓住主要矛盾,忽略次要矛盾。同时体现了严格的hierarchical design。