约 1487 个字 5 张图片 预计阅读时间 7 分钟
Chap 4 | Threads
章节启示录
本章节是OS的第四章。
引入进程的目的是更好地使多道程序并发执行,提高资源利用率和系统吞吐量;而引入线程的目的则是减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能。
线程最直接的理解是“轻量级进程”,是一个基本的CPU执行单元,也是程序执行流的最小单元,由 线程ID、程序计数器、寄存器集合和堆栈 组成
在多线程进程中,需要寄存器和栈为每一个线程进维护。
- Benefits:
- 响应性:交互式应用程序。
- 资源共享:用于代码和数据的内存可以共享。
- 经济:创建进程更昂贵(需要分配资源),而创建线程能节约资源。
- MP架构的利用:多线程提高了并发性。
线程的实现方式¶
线程的实现可以分为两类:用户级线程和内核级线程。
-
用户级线程:“从用户视角能看到的线程”,有关线程管理的所有工作都由应用程序在用户空间内完成,内核意识不到线程的存在。
- 优点:
- 线程切换不需要转换到内核空间,节省开销
- 调度算法可以是某个进程专用的,各个进程可针对自身情况选择合适的调度算法
- 与操作系统平台无关,对线程管理的代码属于用户程序的一部分
- 优点:
-
内核级线程:在内核的支持下运行,线程管理的所有工作在内核空间内实现。
- 优点:
- 能发挥多CPU的优势
- 如果进程中的一个线程被阻塞,内核可以调度该进程中的其他线程占用CPU,也可以运行其他进程中的线程
- 内核支持线程具有很小的数据结构和堆栈,线程切换快、开销小
- 内核本身也可采用多线程技术,可以提高系统的执行速度和效率
- 优点:
ULT(user态中) VS. KLT(kernel态中)
- ULT线程其实是进程内部的划分,不需要Context的转换,只在user层面可见,对于kernel态来说,依然只能看到一个线程。
- ULT并发性不强,其中一个线程被block后另一个线程可能无法进行,但对于KLT来说,其中一个线程被block,另一个线程可以很快地执行。
- 有限性,ULT能创建的线程是有限的。
1.Multithreading Models¶
在同时支持用户级线程和内核级线程的系统中,由于用户级线程和内核级线程连接方式不同,形成了以下三种不同的多线程模型:
Many-to-One¶
线程管理是有效的,但是如果进行系统调用会阻塞,内核一次只能调度一个线程
一个例子🌰
- 班级:Process
- 号码簿/班级号:Process ID
- 跑道:CPU core
- 每条跑道只能有一个运动员
- 运动场可以有一条/多条跑道
- 班级成员:Threads
- 规则
- 每班发一个号码簿
- 成员能跑的戴号码簿上场
- 全班跑完为止
班级内部选一个成员佩戴号码簿,kernel根据号码簿调度场上运动员
One-to-One¶
更高的并发性,但创建线程的成本很高
一个例子🌰
- 班级:Process
- 号码簿:Thread ID
- 班级号:Process ID
- 跑道:CPU core
- 每条跑道只能有一个运动员
- 运动场可以有一条/多条跑道
- 班级成员:Threads
- 规则
- 每人可申请一个号码簿
- 成员戴号码簿上场
- 全班跑完为止
kernel根据号码簿调度场上运动员,一个班可以有多个运动员在跑
Many-to-Many¶
灵活的
一个例子🌰
- 班级:Process
- 号码簿(1班x号):LWP ID
- 班级号:Process ID
- 跑道:CPU core
- 每条跑道只能有一个运动员
- 运动场可以有一条/多条跑道
- 班级成员:Threads
- 规则
- 每班限发若干个号码簿
- 成员能跑的戴号码簿上场
- 全班跑完为止
班级内部指定号码簿给谁戴,kernel根据号码簿调度场上运动员
Two-level Model¶
2.Threading Issues¶
-
fork()只复制调用线程还是复制所有线程?
- 一些unix系统有两个版本的fork(),一个复制所有线程,另一个复制调用fork()的线程。
- Exec()将替换整个进程。
-
Thread Cancellation:在线程完成之前终止它,两种一般方法:
- Asynchronous cancellation(异步取消):立即终止目标线程。可能导致泄露和不一致
- Deferred cancellation(延迟取消):允许目标线程通过标志定期检查是否应该取消。
-
Signal Handling
-
在UNIX系统中,信号用于通知进程某个特定事件已经发生
-
信号处理程序用于处理同步或异步信号:
- 信号是由特定事件产生的
- 信号被传送到一个进程
- 信号被处理
- Options:(传送方式依讯号类型而定)
- 将信号传递给应用该信号的线程
- 将信号传递给进程中的每个线程
- 将信号传递给进程中的某些线程
- 指定一个特定的线程来接收进程的所有信号
-
-
Thread Pools:在一个线程池中创建一些等待工作的线程
- 优点:
- 使用现有线程处理请求通常比创建新线程快一些
- 允许将应用程序中的线程数绑定到池的大小
- 优点:
-
Thread Specific Data(TLS)
- 允许每个线程拥有自己的数据副本
- 在某些方面类似于静态数据,但对每个线程都是唯一的
- 当无法控制线程创建过程时(例如使用线程池时)非常有用。