操作系统基础10-线程及多线程模型
2020-10-23 14:42·重学IT的老猫
什么是线程
线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
线程与进程关系
在早期的操作系统中并没有线程的概念,进程是能拥有资源和独立运行的最小单位,也是程序执行的最小单位。任务调度采用的是时间片轮转的抢占式调度方式,而进程是任务调度的最小单位,每个进程有各自独立的一块内存,使得各个进程之间内存地址相互隔离。
后来,随着计算机的发展,对CPU的要求越来越高,进程之间的切换开销较大,已经无法满足越来越复杂的程序的要求了。于是就发明了线程,线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。一个进程可以有一个或多个线程,各个线程之间共享程序的内存空间(也就是所在进程的内存空间)。
一个标准的线程由线程ID、当前指令指针(PC)、寄存器和堆栈组成。而进程由内存空间(代码、数据、进程空间、打开的文件)和一个或多个线程组成。
单线程与多线程
引入线程机制后的变化
- 资源分配、调度
传统进程机制中,进程是资源分配、调度的基本单位,引入线程后,进程是资源分 配的基本单位,线程是调度的基本单位。
- 并发性
传统进程机制中,只能进程间并发,引入线程后,各线程间也能并发,提供了并发度。
- 系统开销
传统进程间并发,需要切换进程的运行环境,系统开销很大,线程间并发,如果是同一进程内线程切换,则不需要切换进程环境,系统开销小。线程上下文切换比进程上下文切换要快得多,因此,引入线程后,并发所带来的系统开销减小。
线程的实现
分为两大类:用户级线程、内核级线程
- 用户级线程(User-Level Thread, ULT)
用户级线程
用户级线程由应用程序通过线程库实现。所有的线程管理工作都由应用程序负责(包括线程切换)。用户级线程中,线程切换可以在用户模式下即可完成,无需操作系统内核支持。在用户看来,是有多个线程。但在操作系统内核看来,并未意识到线程的存在。可以这样理解,用户级线程就是从用户视角看所看到的线程。
- 内核级线程(Kernel-Level Thread, KLT)
内核级线程的管理工作由操作系统内核完成。线程调度、切换等工作都有内核负责,因此内核级线程的切换必然需要在内核模式下才能完成。可以这样理解,内核级线程就是从操作系统内核视角看所看到的线程。
内核级线程
在同时支持用户级线程和内核级线程的系统中,可采用二者组合的方式:将n个用户级线程映射到m个内核级线程上(n>=m)
操作系统只“看得见”内核级线程,因此只有内核级线程才是处理机分配的单位。
如下这个模型:该进程有两个内核级线程,三个用户级线程,在用户看来,这个进程中有三个线程。但即使该进程在一个4核处理机的计算机上运行,也最多只能被分配到两个核,最多只能有两个用户线程并行执行。
用户级线程与内核级线程的组合
多线程模型
上面提过的,在同时支持用户级线程和内核级线程的系统中,用户级线程和内核级线程之间存在映射关系,这引出了“多线程模型”问题。
- 多对一模型
多对一模型
多对一模型映射多个用户级线程到一个内核级线程。每个用户级线程只对应一个内核级线程。线程管理是由用户空间的线程库来完成,因此效率更高。但是,如果一个线程执行阻塞系统调用,那么整个进程将会阻塞。再者,因为任一时间只有一个线程可以访问内核,所以多个线程不能并行(**parallel)**运行在多个处理核系统上。
- 一对一模型
一对一模型
一对一模型映射每个用户级线程到一个内核级线程。该模型在一个线程执行阻塞系统调用时,能够运行另一个线程继续执行,所以它提供了比多对一模型更好的并发功能;它也允许多个线程并行运行在多核处理器系统上。这种模型唯一的缺点是,创建一个用户线程就要创建一个相应的内核线程。由于创建内核线程的开销会影响应用程序的性能,所以这种模型的大多数实现限制了系统支持的线程数量。Linux、Windows操作系统的家族,都实现了一对一模型。
- 多对多模型
多对多模型
多对多模型:n个用户级线程映射到m个内核级线程(n>=m)。每个用户级线程对应同样数量或更少数量的内核级线程。该模型克服了多对一模型并发度不高的缺点,又克服了一对一模型中创建内核开销太大的缺点。可以说多对多模型集多对一、一对一模型二者的所长。
线程库
线程库(thread library)为程序员提供创建和管理线程的API。实现线程库的主要方法有两种。
第一种方法:在用户空间中提供一个没有内核支持的库。这种库的所有代码和数据结构都位于用户空间中。调用该库内的一个函数只是导致了用户空间内的一个本地函数的调用,而不是内核的系统调用。
第二种方法:实现由操作系统直接支持的内核级的一个库。库内的代码和数据结构位于内核空间。调用库中的一个API函数通常会导致对内核的系统调用。
三种主要线程库:Pthreads、Windows、Java
Pthreads作为POSIX标准的扩展,可以提供用户级和内核级线程库。可移植操作系统接口(Portable Operating System Interface,缩写为POSIX),POSIX是由IEEE的一组标准组成,其目标是提供一套大体上基于Unix的可移植操作系统标准。Linux是与POSIX兼容的。
Windows线程库是用于Windows操作系统的内核级线程库。
Java线程API允许线程在Java程序中直接创建和管理。由于Java虚拟机(JVM)实例运行在宿主操作系统之上,Java线程API通过采用宿主系统的线程库来实现。意味着在Windows系统上,Java线程通过采用Windows API来实现,而在UNIX和Linux系统中采用Pthreads来实现。