【Windows核心编程学习笔记】线程调度、优先级和关联性
一、线程调度
我们知道,每个线程都有一个上下文(CONTEXT),后者保存在线程的内核对象中。这个上下文反映了线程上一次执行时CPU寄存器的状态。大约每隔20ms,Windows会查看所有当前存在的线程内核对象。在这些内核对象中,只有一些被认为是可调度的。Windows从可调度的线程内核对象中选择一个,并将上次保存在线程上下文中的值载入CPU寄存器。这一操作被称为上下文切换(context switch)。载入线程上下文、让线程运行、保存上下文并重复的操作在系统启动时就开始,然后这样的操作会不断重复,直至系统关闭。
系统采用CONTEXT结构记住线程的状态,这样线程在下一次获得CPU可以运行时,就可以从上次停止处继续运行。在Windows定义的所有数据结构中,CONTEXT是唯一一个特定于CPU的。
需要注意的是:系统只调度可调度的线程。但是事实上,系统中大多数线程都是不可调度的。比如:被挂起的线程,等待某种事件发生的线程等等。
二、线程的挂起和恢复
在线程的内核对象中有一个值表示线程的挂起记数。调用CreateProcess或者CreateThread时,系统将创建线程内核对象,并把挂起记数初始化为1.这样,就不会给这个线程调度CPU了。这正是我们希望的,因为线程初始化需要时间,我们并不想在线程准备好之前就开始执行。在线程初始化之后,CreateProcess或者CreateThread函数会查看是否有CREATE_SUSPENDED标志传入,如果有,函数会返回并让新的线程处于挂起状态。如果没有,函数会将线程的挂起记数递减为0.当线程的挂起记数为0时,线程就成为可调度的了,除非它还在等待某个事件发生。
通过创建一个处于挂起状态的线程,我们可以在线程执行任何代码之前改变它的环境。改变了线程的环境之后,必须使其变为可调度的:可以通过调用ResumeThread函数,传入调用CreateThread时所返回的线程句柄(或者传给CreateProcess的ppiProcInfo参数所指的结构中的线程句柄)予以实现。
线程相对
优先级
进程优先级类
Idle
Below Normal
Normal
Above Normal
High
Real-Time
Time-critical
15
15
15
15
15
31
Highest
6
8
10
12
15
26
Above normal
5
7
9
11
14
25
Normal
4
6
8
10
13
24
Below normal
3
5
7
9
12
23
Lowest
2
4
6
8
11
22
Idle
1
1
1
1
1
16
需要注意的是,进程永远无法调度,能调度的是线程。进程优先级是Microsoft提出的一个抽象概念,有助于用户无需了解调度程序的内部工作机理,别无他意。
调用CreateProcess时,可以在fdwCreate参数中传入需要的优先级。
动态提升线程优先级
系统通过线程的相对优先级加上线程所属的进程的优先级来确定线程的优先级值,有时候这也称为线程的基本优先级值。偶尔,系统也会提升一个线程的优先级---通常是为了响应某种I/O事件比如窗口消息或者磁盘读取。
注意,线程的当前优先级不会低于线程的基本优先级。而且使线程可调度的设备驱动程序能够决定提升的幅度。系统只能体素优先级值在1-15的线程(这个范围被称为动态优先级范围)。
另一种情况也会导致系统动态提升线程的优先级:当系统检测到右线程已经处于饥饿状态3到4秒时,会动态将饥饿线程的优先级提升为15.并允许该线程运行2个时间片。当两个时间片结束之后,线程的优先级立即恢复到基本优先级。