1、为什么要引入线程?
前文中进程的引入,使得系统中各个程序可以并发执行,可是虽然进程允许操作系统并发执行多个程序,但在某些应用场景下,比如QQ想要同时实现文件传输和视频聊天,单一进程内部也需要实现并行处理不同的任务。这就引出了线程的概念。
可以把线程理解为“轻量级进程”。
线程是一个 基本的CPU执行单元, 也是 程序执行流的最小单位。 引入线程之后,不仅是进程之间可以并发,进程内的各线程之间也可以并发,从而进一步提升了系统的并发度,使得一个进程内也可以并发处理各种任务(如QQ 视频、文字聊天、传文件)引入线程后,进程只作为除CPU之外的 系统资源的分配单元(如打印机、内存地址空间等都是分配给进程的)。
线程则作为处理机的分配单元。
2、引入线程机制之后的变化
2.1 资源分配、调度上
传统进程机制中,进程是资源分配、调度的基本单位,引入线程后,进程是资源分配的基本单位,线程是调度的基本单位。
2.2 并发性上
传统进程机制中,只能进程间并发,引入线程后,各线程之间也能并发,提高了并发度。
2.3 系统开销
传统进程间并发,需要切换进程的运行环境,系统开销大。
线程间并发,如果是同一进程内的线程切换,则不需要切换进程环境,系统开销小。引入线程后,并发所带来的系统开销小。
为了更好地理解进程和线程在并发执行时的系统开销差异,我们可以用一个生活中的例子来形象地描述这种变化。假设你经营一家餐厅,来看看如何通过这个场景来类比进程和线程的工作方式。
进程间的并发(高开销)
想象你的餐厅有多个独立的厨房,每个厨房相当于一个进程。每个厨房有自己的厨师、厨具、食材和空间,彼此完全独立。如果你需要在不同的厨房之间切换任务(例如从一个厨房转到另一个厨房去准备不同的菜肴),你需要:
- 离开当前厨房:这意味着你需要停止当前的工作。
- 移动到另一个厨房:这可能需要穿过餐厅或者走一段距离。
- 重新开始工作:到达新厨房后,你需要熟悉新的环境,找到所需的工具和材料,然后才能继续工作。
每次切换厨房都会耗费时间和精力,这就是为什么进程间切换会带来较大的系统开销——因为你需要切换整个运行环境(地址空间、资源等)。
线程间的并发(低开销)
现在,假设你只有一个大厨房(相当于一个进程),但在这个厨房里有多个厨师(相当于多个线程)。这些厨师共享同一个厨房的所有资源,包括厨具、食材和空间。在这种情况下,如果你需要在不同任务之间切换(比如从切菜转到炒菜),你只需要:
- 在同一厨房内移动:不需要离开厨房或穿越餐厅。
- 更换任务:因为所有资源都在同一个地方,你可以快速找到所需的工具和材料,立即开始新的任务。
由于所有的厨师都在同一个厨房里工作,并且共享相同的资源,因此他们之间的任务切换非常迅速和高效。这就类似于同一进程内的线程切换,因为它们共享相同的内存空间和其他资源,所以切换成本很低。
总结
- 进程间的并发就像在不同的独立厨房之间切换,每次切换都需要大量的准备工作,导致较高的系统开销。
- 线程间的并发则像是在同一个厨房内部的不同任务之间切换,因为共享资源,所以切换起来更加迅速和高效,带来的系统开销较小。
通过这个餐厅的例子,我们可以更直观地理解为什么引入线程后的并发机制能够显著减少系统开销,并提高系统的整体性能和响应速度。
3、线程的实现方式
3.1 用户级线程(User-Level Threads, ULT)
用户级线程是指线程的管理完全由用户态的库函数来完成,操作系统内核并不知道这些线程的存在。这类线程的创建、调度和同步等操作都由应用程序自己负责。
“用户级线程”就是 “从用户视角看能看到的线程”,操作系统内核并不能意识到线程的存在。
优缺点
优点:用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高
缺点:当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高。多个线程不可在多核处理机上并行运行。
3.2 内核级线程(Kernel-Level Threads, KLT)
内核级线程是指线程的管理由操作系统内核来完成。每个线程都是一个独立的调度单位,可以直接被内核调度到不同的CPU核心上运行。
“内核级线程”就是 “从操作系统内核视角看能看到的线程”
优缺点
优点:当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核处理机上并行执行。
缺点:一个用户进程会占用多个内核级线程, 线程切换由操作系统内核完成,需要切换到核心态,因此线程管理的成本高,开销大。
4、多线程模型
4.1 一对一
一个用户级线程映射到一个内核级线程。每个用户进程有与用户级线程同数量的内核级线程。
优点:当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核处理机上并行执行。
缺点:一个用户进程会占用多个内核级线程, 线程切换由操作系统内核完成,需要切换到核心态,因此线程管理的成本高,开销大。
4.2 多对一
多个用户级线程映射到一个内核级线程。且一个进程只被分配一个内核级线程。
优点:用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高
缺点:当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高。多个线程不可在多核处理机上并行运行
重点重点重点:操作系统只“看得见”内核级线程,因此只有内核级线程才是处理机分配的单位。
4.3 多对多
n 个用户及线程映射到 m 个内核级线程(n >= m)。每个用户进程对应 m 个内核级线程。
克服了多对一模型并发度不高的缺点(一个阻塞全体阻塞),又克服了一对一模型中一个用户进程占用太多内核级线程,开销太大的缺点。
可以理解为:用户级线程是“代码逻辑”的载体,而内核级线程是“运行机会”的载体,一段“代码逻辑”只有获得了“运行机会”才能被CPU执行。
5、线程的状态与转换
这与之前的进程的状态与转换基本类似,同时我们只关注最核心的三个状态,即就绪态、运行态和阻塞态。
6、线程的组织与控制
与进程控制块PCB类似