概念

线程是进程内一个相对独立、可调度的执行单元。

多种线程的定义:

  • 进程内的一个执行单元。
  • 进程内的一个可调度实体。
  • 程序(或进程)中相对独立的一个控制流序列。
  • 执行的上下文。

性质

  • 线程是进程内一个相对独立的可执行单元(子任务)。
  • 线程是操作系统的基本调度单元,包含调度信息。
  • 一个进程至少有一个线程,进程本身不是基本调度单元。
  • 线程可以创建线程,形成线程族系。
  • 进程是拥有资源的基本单位,多线程共享进程资源。
  • 线程间需要通信和同步机制以解决资源共享带来的矛盾。
  • 线程有其生命周期(从建立到结束,伴随状态变化)。

和进程的比较

调度

  • 同一进程内的线程切换不会引起进程切换。
  • 不同进程间的线程切换才会导致进程切换。

并发性

  • 进程之间可以并发执行。
  • 一个进程内的多个线程同样可以并发执行。

系统资源

  • 进程是资源分配的基本单位(资源拥有者)。
  • 线程共享其所属进程拥有的全部资源。

系统开销

  • 进程的创建和撤销开销大于线程。
  • 进程切换的开销大于同一进程内的线程切换开销。

优点

  • 降低了系统开销。
  • 增加了通信的有效性。
  • 方便和简化了用户程序结构。

使用情况

  • 前后台工作分离。
  • 异步处理任务。
  • 需要提高执行速度的场景。
  • 组织结构复杂的工作。
  • 需要同时响应多个用户服务请求的场景。

线程的分类

传统上,线程分为 内核级线程用户级线程

用户级线程

早期的线程均为用户级。为了管理用户级线程,操作系统提供一个在用户空间执行的 线程库。该库是一组供应用程序共享的软件包,负责在 用户态 完成线程的创建、调度和撤销。对于操作系统内核而言,进程中的线程是 透明的,内核只对进程进行管理。

当一个线程被派生时,线程库为其生成线程控制块(TCB)等数据结构。其处理过程与进程创建类似,但有以下不同:

  • 用户级线程的调度只进行 线程上下文切换,不涉及处理器状态的改变。
  • 由于用户级线程的上下文切换与内核无关,当一个进程因 I/O 中断等原因被阻塞时,其内部的其他线程也随之被阻塞。

线程的三个状态

  • 就绪状态:线程已具备运行条件,等待分配 CPU。
  • 运行状态:线程正在 CPU 上运行。
  • 等待状态(阻塞状态):线程正在等待某一事件发生。

补充说明

  • 用户级线程通常没有挂起状态,因为它们共享进程的内存空间。
  • 理论上,一个线程阻塞不应影响其他线程,但在纯用户级模型中,若阻塞发生在内核层面(如系统调用),则整个进程都会被阻塞。

用户级线程优点

  • 开销小:进程内线程切换无需从 用户模式 → 内核模式、再由 内核模式 → 用户模式 的转换,减少了系统开销。
  • 调度灵活:线程切换算法可由应用程序自行定义(如简单轮转或优先级算法),且不会干扰内核的进程调度。
  • 通用性强:用户级线程可以在任何操作系统上运行,无需内核支持。

核心级线程

核心级线程(或称内核级线程)由操作系统内核直接管理,其线程控制块(TCB)存放在 操作系统空间 内。线程的创建、调度和状态转换均由操作系统通过 系统调用 完成。

与用户级线程不同,核心级线程既可以在一个处理器上 并发 执行,也可以被调度到不同处理器上 并行 执行。操作系统内核统一负责进程和线程的调度,因此不会出现整个进程因单个线程的阻塞而停滞的情况。

核心级线程的上下文切换开销大于用户级线程。

用户级线程的两个主要缺点也正是核心级线程的优点:

  1. 当一个线程执行阻塞型系统调用时,内核可以调度该进程内的其他线程继续执行,而不是阻塞整个进程。
  2. 在多处理器系统上,内核可以将同一进程的多个线程分配到不同的处理器上,真正实现并行计算,充分利用多处理技术。

总结

操作系统的进程和线程设计,可以归结为三点:

  1. 多进程 形式,允许多个任务同时运行。
  2. 多线程 形式,允许单个任务分成不同的部分并发运行。
  3. 提供 协调机制,一方面防止进程和线程之间产生冲突,另一方面允许它们共享资源。

线程同步

操作系统会随机调度线程,程序员无法预知线程的执行顺序。在以下两种情况下,线程间需要同步:

  • 多个线程访问共享资源时,为防止数据损坏,需要保证访问的排他性(互斥)。
  • 一个线程需要将任务完成情况通知其他一个或多个线程时,需要协调执行顺序(同步)。

Windows 提供的线程同步方法主要有 临界区、互斥量、事件、信号量

临界区 (Critical Section)

  • 定义:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。
  • 机制:确保在某一时刻只有一个线程能进入临界区。当一个线程进入后,其他任何试图进入的线程都将被挂起,直到该线程离开。临界区被释放后,其他线程可继续争抢。
  • 原则
    • 一次最多只能有一个线程停留在临界区内。
    • 线程不能无限地停留在临界区内,以免其他线程饿死。

互斥量 (Mutex)

  • 定义:与临界区类似,但更为强大。只有拥有互斥对象的线程才能访问特定资源。
  • 机制:互斥对象是唯一的,确保了任何时候只有一个线程能访问资源。任务完成后,线程必须释放互斥对象,以便其他线程获取。线程结束时,其拥有的互斥量会自动释放。
  • 应用:互斥量不仅可以在同一进程的不同线程间使用,还可以在 不同进程的线程之间 实现资源安全共享,这是它与临界区的主要区别。常用于保护共享内存块。

事件 (Event)

  • 定义:通过通知操作的方式来保持线程同步。一个线程可以等待一个事件被触发,而另一个线程则可以在适当的时候触发该事件。
  • 机制:事件对象有两种状态:受信(signaled)和未受信(non-signaled)。线程可以等待一个事件变为受信状态再继续执行。
  • 应用:主要用于标志某个事件的发生,并以此协调线程的执行顺序,可以方便地实现一对多或多对多的线程通信。

信号量 (Semaphore)

  • 定义:允许多个线程同时访问同一共享资源,但会限制同时访问的线程最大数量。
  • 机制:信号量维护一个计数器。当线程要访问资源时,计数器减一;当线程释放资源时,计数器加一。若计数器为零,则后续请求资源的线程将被阻塞,直到有其他线程释放资源。这与操作系统中的PV操作原理相同。
  • 应用:适用于需要限制并发访问数量的场景,例如数据库连接池。