背景知识:

  • 调度:就是选出下一个要在cpu上执行的线程。
  • 优先级:“剥夺”式调度的依据,windows将线程的优先级分为32个级别,0最低,31最高
  • 就绪队列:windows系统中,每一个cpu32个队列,每一个队列对应一个优先级,一个线程变成可调度,要么成为standingby线程,要么会按照他的优先级挂入相应队列的尾部。
  • Standingby线程:当前线程的“剥夺”者,马上会得到cpu的执行权。

我们知道,WindowsNT以线程为调度单位,调度策略为抢占式加时间片轮转,Windows严格按优先级调度,即:
  • 高优先级的线程将立刻抢得cpu
  • 较高优先级的程序不退出调度,较低优先级的程序将不会得到cpu
  • cpu上运行的线程被赋予一个时间片,除非被高优先级的线程“剥夺”,将一直运行下去,直到时间片用完
  • 如果有相同优先级可调度线程存在,用完时间片的线程将被时钟剥夺cpu

我原来有一个错误认识,认为线程的调度和切换只发生在时钟中断例程中,所以猜测,为了体现抢占调度的特性,调度周期一定非常的短,和线程的时间片不是一个数量级。现在想来,该猜想是靠不住的,理由如下:
  • 时钟中断不应该太频繁,影响系统性能。
  • 认为只有时钟中断中才可以进行线程调度和切换是毫无道理的。
  • 通过编程发现我的系统的时钟中断间隔15.625毫秒(可以通过UserSharedData.TickCountMultiplier 计算得到),我们知道,普通线程的时间片也在这个数量级。
通过阅读WRK和毛德操先生的Windows内核情景分析发现,真实情况原来是这样的:
 
 
 

一、线程的调度:
  • 当前线程主动让出剩余的运行时间
    该调度是当前线程主动让出剩余运行时间,如执行了SleepWait一个没有授信的对象等等
  • 有新的线程变得可调度
    比如一个被挂起的线程被中断处理程序激活,变为可调度,要进行一次调度,中断处理程序一般会将该线程的优先级和当前线程比较,如果优先级较高,该线程将成为抢夺者,否则该线程按照其优先级挂入相应的就绪队列。
  • 就绪线程的优先级提高
    如当前线程将一个就绪线程的级别提高,将引发一次调度,如果该线程的优先级比当前cpu上的线程的优先级高,该线程就成为剥夺者。
  • 调度时钟中断
    每一次时钟中断,都会将当前线程的定量减少一定值,如果发现当前线程的时间片已经用完,就会进行一次调度,如果和当前线程优先级对应的就绪队列非空,该排在该就绪队列最面的线程将会得到调度,成为即将运行的线程。
二、线程的切换:
Windows是在调度后马上切换到选出的线程么?不一定,主动的调度和线程切换是连在一起的,调度完马上进行线程切换,但是“剥夺”式调度和线程切换是分离的,要等到CPU运行级别降到DISPATCH_LEVEL一下,才允许线程切换,因为线程的切换将导致堆栈的切换,而中断中是不允许堆栈切换的,可能这就是“剥夺”式调度和线程切换分离的原因吧。
线程切换时,如果切换前后的线程不属于同一个进程,还会引发一次进程切换,关于进程切换就不说了。
 
以上是我对windows线程的调度和切换的肤浅理解,由于水平有限,一定有不全的和错误的,欢迎指正,谢谢!