4.2.5 线程的优先级与调度
Hello China当前版本的实现中,线程的调度算法是抢占式的基于优先级调度算法。在核心线程对象中,有两个变量。
DWORD dwThreadPriority; DWORD dwScheduleCounter;
这两个变量是实现线程调度的基础。
在当前版本的实现中,只实现了一种算法,即优先级相关的时间片调度算法,该算法执行流程如下。
① 线程创建时,dwScheduleCounter初始化为dwThreadPriority,即线程优先级的值。
② 线程每运行一个时间片(系统时钟中断间隔),调度程序把dwScheduleCounter的值减一,并作为新的优先级。
③ 如果dwScheduleCounter减1后,结果是0,那么重新初始化为dwThreadPriority的值。
根据这个算法,优先级越高的线程获得的CPU资源越多,而且优先级高的线程可以完全抢占优先级低的线程而投入运行。但这个算法也避免了优先级低的线程出现所谓“饿死”现象,即CPU时钟被优先级高的线程占有,优先级低的线程无法占有CPU而得不到运行。因为随着运行时间的增加,优先级高的线程,其dwScheduleCounter逐渐降低,在降低到一定程度时,优先级低的线程就有机会运行了。
比如,按照这个算法,系统中存在三个线程。
● 线程A,优先级为6;
● 线程B,优先级为4;
● 线程C,优先级为2。
假设A先运行,按照这个算法,系统将发生以下进程切换动作。
(1)第一个时钟中断:调度程序减少A的dwScheduleCounter(初始为6,结果为5),然后跟Ready队列的第一个线程(B)比较,发现A比B的优先级(dwScheduleCounter)大,于是继续运行A。
(2)第二个时钟中断:调度程序减少A的dwScheduleCounter,然后与Ready队列的第一个线程(B)比较,发现仍然不小于(只有小于才发生切换),于是继续运行A。
(3)第三个时钟中断:调度程序减少A的dwScheduleCounter,跟B比较,发现小于B,于是A入Ready队列(以dwScheduleCounter为关键字,此时为3),然后恢复B的上下文,B开始运行。
(4)第四个时钟中断:调度程序减少B的dwScheduleCounter(结果为3),然后跟Ready队列中的第一个线程(A)比较,发现不小于,于是继续运行。
(5)第五个时钟中断:调度程序减少B的dwScheduleCounter(结果为2),然后跟A比较,发现小于,于是B入Ready队列,恢复A上下文,A继续运行。
(6)第六个时钟中断:调度程序减少A的dwScheduleCounter(结果为2),然后跟Ready队列的第一个线程(C)比较,发现不小于,于是继续运行。
(7)第七个时钟中断:调度程序减少A的dwScheduleCounter(结果为1),然后跟C比较,发现小于C,于是C运行。
这三个线程运行的序列如图4-5所示。
图4-5 线程运行时序
通过上面的描述可以看出,该算法是严格抢先的,比如,又有一个核心线程D被创建,D的优先级为8,那么,在下一个调度时刻(时钟中断),D将会被优先选择执行,因为D的优先级在当前就绪线程队列中是最高的。
需要注意的是,上述算法仅仅是Hello China可以实现的调度算法中的一种,读者完全可以根据自己的需要,开发出适合自己应用的另外的调度算法。比如,上述调度算法为了兼顾低优先级的线程,采用了“优先级递减”的思路,随着高优先级任务的不断运行,其调度优先级将持续递减,直到为0,然后又恢复为初始值。但这样的调度算法,有的情况下可能满足不了需求。比如,有的系统要求严格优先,即优先级高的线程,将一直保持高优先级,不向低优先级的线程让步,这样读者就可以把优先级递减相关的代码删除,而实现一种严格优先的调度算法。
另外,为了实现上的方便,虽然dwThreadPriority可以取任何整数值,但为了规范起见,目前版本的Hello China,只定义了下列几个可取值。
#define PRIORITY_LEVEL_REALTIME 0x00000020 //Kernel thread's priority level. #define PRIORITY_LEVEL_CRITICAL 0x00000010 #define PRIORITY_LEVEL_IMPORTANT 0x00000008 #define PRIORITY_LEVEL_NORMAL 0x00000004 #define PRIORITY_LEVEL_LOW 0x00000002 #define PRIORITY_LEVEL_LOWEST 0x00000001 #define PRIORITY_LEVEL_INVALID 0x00000000
线程的优先级可以在线程创建(CreateKernelThread函数)的时候指定,建议应用程序在赋予线程优先级的时候,只采用上述定义值。