本篇主要摘自《程序员自我修养–链接、装载与库》第1.6节 众人拾柴火焰高 部分。
线程基础
- 线程(Thread),也称轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。
- 通常意义上,一个进程由一个或多个线程组成,各线程之间共享程序的内存空间(包括代码段、数据段、堆等)以及进程级的资源(如打开文件和信号)。
一个经典的线程与进程的关系如图所示
线程的访问权限
线程的访问非常自由,它可以访问进程内存里的所有数据,甚至包括其他线程的堆栈(如果它知道其他的堆栈地址,那么就是很少见的情况),但实际运用中线程也拥有自己的私有存储空间,包括以下几个方面:
- 栈(尽管并非完全无法被其他线程访问,但一般情况下仍然可以认为是私有的数据)
- 线程局部存储(The Local Storage,TLS)。线程局部存储是某些操作系统为线程单独提供的私有空间,但通常只具有很有限的容量。
- 寄存器(包括PC寄存器),寄存器是执行流的基本数据,因此为线程私有。
从C程序员的角度来看,数据在线程之间是否私有如表所示:
线程私有 | 线程之间共享(进程所有) |
---|---|
局部变量 | 全局变量 |
函数的参数 | 堆数据 |
TLS数据 | 函数中的静态变量 |
程序代码,任何线程都有权利读取并执行任何代码 | |
打开的文件,A线程打开的文件,B线程可以读写 |
线程调度与优先级
-
无论是单处理器还是多处理器,线程总是“并发”执行的,在线程数量少于处理器数量时,各个线程运行在不同的处理器上,互不干扰,但是当线程数量多于处理器数量时,线程的并发会受到阻碍,因为一个处理器上要运行多个线程。
-
当一个处理器上要处理多个线程时,操作系统会让这些多线程程序轮流执行,每一次执行很少时间,这样每个线程看起来是同时进行的,这样的一个在处理器上切换不同的线程的行为叫做线程调度。
- 在线程调度中,线程拥有三种状态:
- 运行(Running):此时线程正在执行
- 就绪(Ready):此时线程可以立即运行,但CPU已经被占用。
- 等待(waiting):此时线程正在等待某一事件(通常是I/O或同步)发生,无法执行
- 运行(Running):此时线程正在执行
- 处于运行中的线程拥有一段可以执行的时间,这段时间被称为时间片(Time Slice)。
- 如果时间片用尽时,该线程进入就绪状态。
- 如果在时间片用尽之前线程就开始等待某事件,该线程进入等待状态。
- 当一个线程离开运行状态时,调度系统选择一个就绪线程继续执行。
- 当处于等待状态的线程所等待的事件发生后,该线程进入就绪状态。
- 如果时间片用尽时,该线程进入就绪状态。
- 线程调度的方法主要是线程优先级调度,优先级调度决定线程按照什么样的顺序轮流执行,在线程优先级调度的系统中,每个线程都有自己的优先级,优先级高的线程会更早执行,线程优先级决定本线程被执行的早或迟。
- 在线程优先级调度的环境下,线程的优先级被改变有三种方式:
- 1、用户指定优先级。
- 2、根据进入等待状态的频繁程度提升或降低优先级,进入等待状态越频繁,说明线程占用处理器很短时间,这样的线程称之为IO密集型线程,进入等待时间少的线程称之为CPU密集型线程,IO密集型线程比CPU密集型线程容易得到优先级的提升。
- 3、长时间得不到执行的线程,操作系统会提升其优先级。
- 1、用户指定优先级。