Overview / What is a thread?
Multicore Programming
Multithreading Models
Thread Libraries
Implicit threading
Threading issues / Designing multithreaded programs
一、Overview
1. CPU 是计算机中执行二进制代码的硬件组件。
Central Processing Unit
2. OS 是调度(schedules)程序使用 CPU 的软件。
Operating Systems
3. 进程是正在执行的程序。
4. 线程是进程的一部分。
二、thread
(1) 线程是可由调度器独立管理的最小指令序列。
A thread of execution is the smallest sequence of programmed instructions that can be managed independently by a scheduler.
操作系统中的调度器(Scheduler)可以独立地对线程进行管理,包括分配 CPU 资源、调度执行、暂停或终止线程等。
executed by the CPU independently of the parent process(可独立于父进程由 CPU 执行)
线程可以由 CPU 直接执行,即使它属于某个进程,它仍然可以独立运行,不一定要依赖整个进程的调度。
(2) 线程是轻量级(lightweight)进程,可以被调度器独立管理。
(3) 线程按顺序执行指令,一次只执行一项任务。
(4) 线程是进程内的控制流。(a flow of control)
(5) 进程的多个(multiple)线程共享内存。
(6) 多个线程可用于并行执行任务。
1. O.S. view
• 在操作系统视角下,线程是可被独立调度的指令流。
From the O.S. view, a thread is an independent stream of instructions that can be scheduled by the OS.
2. Software developer view
• 在软件开发者视角下,线程是可以独立运行的过程。(procedure)
• 顺序程序:程序中的单一指令流。
Sequential program: a single stream of instructions in a program.
• 多线程程序:包含多个指令流的程序,可利用多个 CPU 核心。
Multi-threaded program: a program with multiple streams, which are needed to use multiple cores/CPUs.
3. Benefits of Threads
(1)Responsiveness 响应性:如果程序的某部分被阻塞或执行耗时操作(lengthy operation),线程可保证程序继续运行。
(2)Resource Sharing and Economy 资源共享和经济性:线程共享地址空间、文件、代码和数据,减少资源消耗,并提高上下文切换速度。 context switching.
(3)Scalability 可扩展性:
• 代码、文件和数据存储在主存中。
• 线程可分配到不同 CPU。Distribute
• 线程可在不同处理核心上并行运行。
(4)Deadlock avoidance 避免死锁
4. Example of Threads
• 示例:在 Word editor 编辑器中,多个线程执行不同任务:
• 打开文件。
• 输入文本(一个线程) Typing text
• 自动格式化文本(另一个线程) Automatically formatting text
• 自动拼写检查(另一个线程) Automatically checking spelling mistakes
• 自动保存文件(另一个线程) Automatically saving the file to disk
5. Threads & Thread Control Block
(1) 线程在处理器上调度,每个线程独立执行指令。
(2) 线程控制块 (TCB) 存储线程信息
• 线程 ID:唯一标识线程。
• 程序计数器:记录指令地址,指示下一条指令。
• 寄存器集合:存储线程的工作变量。
• 栈:存储线程执行历史(函数调用等)。
• 线程共享进程的代码段、数据段和操作系统资源(如文件和信号)。
6. Thread States - 线程状态
线程控制块(TCB)存储线程信息,如Execution State、Scheduling info、Various Pointers。
• 执行状态 包括 CPU 寄存器、程序计数器和栈指针。program counter, and stack pointer
• 调度信息 包括线程状态、优先级(priority)、CPU 运行时间等。
• 指针 用于调度队列(scheduling queues),可能指向所属的进程控制块(PCB)。
OS keeps track of TCBs in protected memory (in Array, or Linked List)
7. Types of Threads per Process - 进程中的线程类型
1. 单线程进程(Single-thread process):整个进程只有一个线程。
2. 多线程进程(Multi-thread process):进程内有多个线程,同时执行多个任务。
• 多线程进程 允许多个线程共享代码、数据和文件,但每个线程有自己的寄存器和栈。
二、Multicore Programming
Concurrency vs. Parallelism - 并发与并行
1. 并发(Concurrency):在单核 CPU 上,多个任务交替运行,看起来像是同时执行。
OS rapidly switches backand forth between them -> tasks are interleaved The concurrency creates the illusion of parallel execution
2. 并行(Parallelism):在多核 CPU 上,多个任务同时执行,每个核心负责不同任务。
• 多核 CPU 直接运行多个任务,实现 真正的并行。
三、Multithreading Models - 多线程模式
1. 用户模式(User Mode):
应用程序和用户级驱动程序在用户模式下运行。
User mode runs applications and user-mode drivers.
2. 内核模式(Kernel Mode):
操作系统核心组件和内核级驱动程序运行在内核模式。
Kernel mode runs core OS components and kernel-mode drivers.
CPU 根据正在运行的代码类型在两种模式之间切换。
• Windows API(Windows 应用程序编程接口):提供一组接口,允许应用程序与操作系统通信
• Hardware Abstraction Layer(HAL,硬件抽象层)
• HAL 使操作系统能够在不同硬件上运行,而不需要修改内核。
• 它向上层提供一致的硬件访问接口,处理 CPU、内存管理、I/O 设备等。
• Exported Driver Support Routines(驱动支持例程)
• 连接 操作系统内核(Operating System Kernel) 与 用户模式,提供系统调用(System Call)。
• 例如,当应用程序需要访问磁盘数据时,它会通过 API 调用内核的 文件系统驱动(File System Drivers) 处理请求。
3. 如何查看 CPU 运行情况
• 在 Windows 系统中,可以使用 任务管理器(Task Manager) 查看 CPU 运行情况(Ctrl + Alt + Del)。
• 总 CPU 时间(Total CPU Time) 表示 CPU 总体利用率。
• 内核时间(Kernel Time) 代表 CPU 处理操作系统任务的时间。
• 用户时间(User Time) 代表 CPU 处理用户应用程序的时间(总时间 - 内核时间)。
3. Types of threads
(1) 用户线程(User Threads, ULT)
User Level Threads
• 由用户级线程库管理,内核不直接感知。
• primary thread libraries:POSIX Pthreads、Windows 线程、Java 线程、C# 和 Python 线程库。
(2) 内核线程(Kernel Threads, KLT)
Kernel Level Threads
• 由操作系统内核管理,线程调度由内核完成。
• 适用于几乎所有通用操作系统,如 Windows、Linux、Mac OS X、iOS、Android。
4. Difference between ULT & KLT
• 线程切换只涉及用户态资源,无需陷入内核模式,避免了系统调用的开销。
• ULT 不依赖于操作系统,而是由 线程库(如 POSIX Pthreads、Java Threads、Windows Fibers) 在应用层管理。
• 这种实现方式允许 ULT 在不同操作系统上运行,不受操作系统的具体实现限制。
• 操作系统只看到一个进程,并不会把不同 ULT 分配到多个核心上运行,即使有多个线程,它们也只能在一个核心上运行,影响计算密集型任务的性能。
5. Solaris Threads(Solaris 线程模型)
• 用户级线程(ULT) 运行在用户空间,由线程库管理。
• 轻量级进程(LWP,Lightcess) 作为 用户线程与内核线程的桥梁。
• 内核线程(KLT) 由操作系统管理,映射到 CPU 上执行任务。
• LWP 允许多个用户线程共享一个 KLT,提高性能和灵活性。
• Solaris 线程模型结合了 Many-to-One、One-to-One、Many-to-Many 三种模型的优势。
6. Multithreading Models(多线程模型)
多线程映射模型(ULT → KLT)有三种类型:
1. Many-to-One(多个用户线程 → 一个内核线程)
2. One-to-One(一个用户线程 → 一个内核线程)
3. Many-to-Many(多个用户线程 → 多个内核线程)
在“映射用户级线程到内核级线程”的系统中,应用程序中的多个线程可以被操作系统映射到多个处理器上并行运行
multiple threads within the same application can run in parallel on multiple processors.
(1)Many-to-One Model(多对一模型)
• 多个用户级线程(ULT)映射到单个内核线程(KLT)。
• 缺点: 由于只有一个 KLT,不能真正利用多核 CPU,多个 ULT 不能并行执行。
• 优点: 线程切换在用户空间进行,效率高,适用于单核系统或不需要并行计算的任务。
Thread management done at user space, by a thread library
Process A 和 Process B
• 代表两个进程,每个进程有多个用户级线程(ULT)。
• 这些 ULT 由用户空间的运行时系统(Run-time System / Library)管理,而不是由内核直接调度。
(2)One-to-One Model(一对一模型)
每个用户级线程(ULT)映射到一个内核线程(KLT)。
优点:
• 支持真正的并行执行,每个线程可以分配到不同 CPU 核心上运行。
• 当一个线程阻塞时,其他线程仍可继续执行,提高并发性。
内核能够实现线程的创建和管理,并且能够对线程进行调度
缺点:每创建一个线程,都需要创建一个对应的 KLT,导致 内核开销大。
Thread Table(线程表)
• 存储进程内的线程信息,如线程状态、寄存器、栈指针等。
(3)Many-to-Many Model(多对多模型)
• 多个用户级线程(ULT)映射到多个内核线程(KLT)。
优点:
• 内核线程的数量可以针对特定的应用程序或特定的机器进行设定
• 操作系统可以动态创建足够数量的内核线程
• 用户可以创建任意数量的线程,并在多核处理器上并行运行。
多线程模型:
1. Many-to-One(性能好但不能并行)
2. One-to-One(真正并行但开销大)
3. Many-to-Many(结合两者优势,灵活管理线程)
四、Thread Libraries(线程库)
线程库提供 API(应用编程接口)用于线程的创建和管理。
线程API(Thread Application Programming Interface)是一组函数,它们是线程库的一部分
• 线程可以在 用户空间 或 内核空间 实现。
线程库(Thread Libraries) 允许程序员通过 API 创建、管理和终止线程,有两种主要类型:
1. 用户级线程库(User-level library)
- 不依赖于内核支持。
▪ 该库的所有代码和数据结构都存在于用户空间。
▪ 调用库中的函数时,执行的是用户空间的本地函数调用,不会触发系统调用。
a local function call
2. 内核级线程库(Kernel-level library)
- 由操作系统直接支持。
▪ 该库的代码和数据结构存在于内核空间。
▪ 调用 API 中的函数通常会触发系统调用(system call),交由内核处理。
system call
主要线程库示例:
• POSIX 线程(Pthreads)
• Java 线程(Java Threads)
• Win32 线程(Windows Threads)
• 用户级线程库 切换快,但无法利用多核 CPU。
• 内核级线程库 开销较大,但支持多线程并行计算。
五、线程管理(Managing Threads)
• 线程管理分为两种方式:显式线程(Explicit Threading) 和 隐式线程(Implicit Threading)。
显式线程(Explicit Threading):由程序员手动创建和管理线程。
隐式线程(Implicit Threading):由编译器和运行时库自动创建和管理线程。
The compiler and runtime libraries handle thread creation and management automatically.
1. 隐式线程(Implicit Threading)
• 设计多线程编程主要有三种实现方式:
Three primary approaches for implicit threading:
1. 线程池(Thread Pool):进程启动时创建固定数量的线程,等待任务分配,提高效率。
Creates a fixed number of threads at process startup, reducing thread creation overhead.
2. OpenMP:适用于 C、C++、Fortran 语言的并行编程编译指令集,自动生成多线程代码。
A set of compiler directives for C, C++, and Fortran that generate parallel code automatically.
3. Grand Central Dispatch (GCD): 是C和C++的扩展,在MacOS X和iOS操作系统上可用,以支持并行计算。
六、Threading Issues in Multithreading environments
1. fork() 和 exec() System Calls
(1) fork() 创建一个新的进程,该进程是原始进程的副本。
fork() creates a new duplicate process.
• fork() 可能只复制调用线程,也可能复制整个进程的所有线程。
(2) exec() 用exec()参数中指定的程序替换程序及其所有线程
exec() replaces the process and all its threads with a new program.
2. 信号处理(Signal Handling)
• 信号(Signal) 是一种软件中断,通知程序发生重要事件。
• Unix 系统支持信号处理,而 Windows 不提供显式支持。
• 信号处理流程:
(1) 事件发生时,生成一个信号。
(2) 信号被传递到目标进程。
(3) 进程收到信号后必须进行处理。
3. 示例
Example 1
非法内存访问(illegal memory access)除零错误(division by zero)
如果一个正在运行的程序执行了这两个操作中的任何一个,就会生成一个信号。信号会被传递给执行了导致信号产生的操作的那个进程。
Example 2: Sending Signals Using The Keyboard /Sending Signals To Processes
• Ctrl + C
send an INT signal (SIGINT) to the running process,默认行为是终止(interrupt)进程。
• Ctrl + Z
send a TSTP signal (SIGTSTP) to the running process,默认行为是暂停(suspend)进程。
• Ctrl + \
send a TSTP signal (SIGTSTP) to the running process,默认行为是终止(terminate/kill)进程。
4. 信号处理(Signal Handling)
• 进程可以通过信号处理程序(signal handler routines)来捕获并处理信号。信号处理器退出后,操作系统将在进程最初被中断的点重新启动进程。
A signal may be handled by one of two possible handlers:
(1)默认信号处理(Default Signal Handler)
• 若程序没有自行处理信号,操作系统会采取默认行为。
• 大多数信号的默认行为是立即终止进程(terminate the process immediately)。
• 少数信号的默认行为是忽略(ignore the signal)。
The default signal handler defines what happens if the signal is not handled by the application.
(2) 用户自定义信号处理(User-Defined Signal Handler)
• 允许用户自定义处理逻辑,例如日志记录、清理资源等。(user-defined)
5. 线程取消(Thread Cancellation)
(1) 异步取消(Asynchronous Cancellation)
• 目标线程(target thread)会被立即终止,但如果它正在修改共享数据,可能会导致数据损坏。
(2) 延迟取消(Deferred Cancellation)
• 目标线程会定期检查是否应该被取消,从而在合适的时机终止。
(3)与线程取消相关的问题(Issues Related to Thread Cancellation)
• 取消的线程可能已经占用了系统资源,若直接终止,可能造成资源泄露。
• 若线程被终止时正在更新共享数据,可能导致数据不一致。