# 线程的基本概念

# 引入线程的动机

引入进程的最初动机是为了使多个程序能够并发执行,从而提高资源利用率和系统吞吐量。进程具备两个基本属性,这也是其能够并发执行的基础:

  • 资源分配单位: 每个进程拥有独立的虚拟地址空间,保存了进程映像(程序代码与数据),并能控制诸如文件、I/O 设备等资源。
  • CPU 调度单位: 进程是程序的一条执行轨迹。

然而,传统的进程模型存在一些挑战:

  • 开销过大: 进程作为资源分配单位,其创建、终止和切换会带来较大的时间和空间开销。这限制了系统中并发进程的数量,也使得进程切换的频率不宜过高,从而制约了系统的并发程度。
  • 多处理器系统挑战: 在多处理器(Multiprocessor)和多核处理器(Chip Multi-processor,CMP)系统中,传统的进程模型难以充分利用并行处理能力。
  • 并发任务需求: 许多现实应用(如字处理程序、Web 服务器)需要同时处理多个任务。

为了解决这些问题,操作系统将进程的两个基本属性分离开来:进程只作为资源分配单位,而线程则作为 CPU 调度单位

# 多处理器系统

多处理器系统可以分为共享存储系统消息传递系统

  • 消息传递系统: 每个处理器拥有独立的局部存储器,处理器之间通过消息传递进行通信。主要包括集群系统和异构计算机系统。

    • 集群系统(Cluster): 每个处理单元都是一台独立的计算机,通过高速网络互连,构成廉价的高性能计算机系统。

  • 共享存储系统: 多个处理器共享一个公用的存储器,并通过它进行通信。进一步可分为 UMA 和 NUMA 结构。

    • UMA(Uniform Memory Access,一致存储器访问): 也称为 **SMP(Symmetric Multi-Processor,对称多处理器)**系统。所有处理器地位平等,可以平等地访问共享的物理内存。

    • NUMA(Non-Uniform Memory Access,非一致存储器访问): 系统由多个节点构成,每个节点有自己的处理器和独立的存储器。所有节点的本地存储器共同组成一个全局地址空间,可被所有处理器访问。

# 多核处理器

多核处理器(Chip multiprocessors,CMP)是共享存储系统的一种发展。随着集成电路技术的发展,多个 SMP 结构可以被集成到同一芯片内,各个处理器可以并行执行不同的线程或进程。

# 线程与进程

  • 线程(Thread): 是进程中能够独立执行的控制流,是处理器调度的基本单位。
  • 多线程(Multithreading): 是指在同一个进程中包含多个并发执行的线程。
  • 线程与进程的关系:
    • 同一进程中的所有线程共享该进程的主存空间资源
    • 在多线程环境下,进程是操作系统中除处理器外进行资源分配和保护的基本单位。它拥有独立的虚拟地址空间,并以进程为单位对各种资源实施保护。
    • 由于处理机调度的基本单位是线程,因此创建进程时,会同时为其创建一个主线程,否则该进程无法被调度执行。

# 线程模型

线程共享其所属进程的地址空间和资源,不同操作系统的进程和线程表现形式以及多线程进程模型与单线程进程模型的示例如下:

  • 线程共享进程的地址空间和资源

  • 不同操作系统中进程和线程的表现形式

  • 多线程进程模型与单线程进程模型

# 线程的属性与控制块

  • 线程的属性:
    • 执行状态(状态转换)
    • 上下文
    • 执行栈:每个线程拥有自己的栈,用于函数调用传参、保存返回地址和存放局部变量。
    • 可存取所在进程的内存和其他资源。
    • 创建、撤消另一个线程。
  • 线程控制块(TCB,Thread Control Block):
    • 是标志线程存在的数据结构,包含系统管理线程所需的所有信息。
    • TCB 中的内容通常较少,因为资源分配等信息已记录在所属进程的 PCB 中。
    • 主要信息包括:线程标识、线程状态、调度参数、CPU 现场信息(如通用寄存器、指令计数器 PC、用户栈指针)。
    • 其存放位置取决于线程的实现方式,可能位于操作系统空间或用户进程空间。

# 线程的状态与比较

  • 线程的状态:
    • 线程有运行、就绪和阻塞三种状态,状态转换类似于进程。
    • 挂起状态对线程没有意义。当进程被挂起并换出主存时,其所有线程也会被换出。因此,挂起是进程级状态。
    • 进程的终止会导致其所有线程的终止。
  • 进程和线程的比较:
特性 进程 线程
地址空间与资源 相互独立 同一进程内共享,且对其他进程不可见
通信 采用 IPC 机制 可直接读写进程数据段(如全局变量),需同步与互斥机制保障数据一致性
调度与开销 上下文切换开销大 上下文切换开销小得多,效率更高
优点 资源独立,一个进程崩溃不影响其他进程 减少并发执行的时间和空间开销,适合多处理机系统,通信更高效
缺点 创建、退出和切换开销大,并发受限 引入程序设计复杂性;一个线程崩溃可能影响整个进程稳定性;线程调度本身也会消耗CPU资源

# 线程的实现机制

线程的实现机制主要分为用户级线程、内核级线程以及混合实现。

# 用户级线程(User Level Thread,ULT)

  • 管理方式: 线程管理由应用程序在用户空间内完成,通过线程库(运行时系统)管理线程。
  • 与内核关系: 操作系统内核不知道线程的存在,它只将处理器分配给进程。
  • 特点:
    • 线程切换不需要核心态特权。
    • 线程调度由应用本身决定。
    • 每个进程有一个专用的线程表,由线程库管理,用于跟踪进程中线程的状态。
    • 当一个线程调用阻塞型系统调用时,整个进程会阻塞

  • 优点:
    • 性能良好: 线程切换不涉及内核,开销小。
    • 灵活性高: 调度是应用程序特定的,可针对应用优化。
    • 可移植性强: 只要有线程库,无论操作系统内核是否支持线程,ULT 均可运行。
  • 缺点:
    • 调度规则简单: 通常采用非抢先式调度。
    • 阻塞问题: 线程调用阻塞型系统调用会导致整个进程阻塞。
    • 无法并行: 同一进程的两个线程不能同时在多处理器上运行,因为内核只将处理器分配给进程。

# 内核级线程(Kernel Level Thread,KLT)

  • 管理方式: 线程管理由操作系统内核完成。
  • 与内核关系: 在内核中有一个线程表,记录系统中所有线程的 TCB,因此不需要用户级的线程库。
  • 特点:
    • 内核能够同时调度同一进程中的多个线程并行执行。
    • 阻塞在线程一级完成,一个线程阻塞不会影响同一进程中其他线程的运行。
    • 内核自身也可以采用多线程技术。

  • 优点:
    • 多核并行: 能够充分利用多处理器的并行能力。
    • 抗阻塞: 一个线程阻塞不影响同一进程中其他线程的运行。
  • 缺点:
    • 系统开销大: 线程调度和管理由内核实现,需要频繁进行模式切换,开销较大。

# 混合实现

  • 模型: 结合了用户级线程和内核级线程的优点。内核只识别并调度内核级线程,而用户级线程则映射到这些内核级线程上。
  • 特点:
    • 灵活性高: 程序员可以根据需要调整内核级线程的数量,以达到最佳性能。
    • 线程的创建、调度和同步都在用户级完成。
    • 应用程序的多个 ULT 会被映射到少数几个 KLT 上。

# Solaris 线程模型

  • 模型结构: Solaris 实现了三层线程结构:用户级线程(ULT)、轻量级进程(LWP)和内核级线程(KLT)
  • LWP(Light Weight Process):
    • 一种由内核支持的用户线程,是内核线程的高级抽象。
    • 每个 LWP 由一个内核线程支持,并由内核独立调度,能够在多处理器上并发执行。
    • LWP 作为 ULT 和 KLT 之间的桥梁,一个 LWP 可以支持多个 ULT。
  • 优点: 实现了 ULT 到 KLT 的多对多映射,灵活高效。
  • 现状: 实现过于复杂,在 Solaris 10 之后被弃用。