l CPU切换调度线程:
每个线程都有一个上下文(CONTEXT),保存在线程的内核对象中。它记录了线程上一次执行时CPU寄存器的状态。
大约每隔20ms,CPU会查看当前所有的线程,然后在可调度的线程中选择一个进行调度。CPU切换调度线程时,先将CPU寄存器写回线程上下文(这样,下次调度该线程,就能从上次停止的地方继续),停止运行原线程;检查剩下的可调度线程内核对象,选择另一个线程内核对象,将该线程上下文载入CPU寄存器。
查看和设置CONTEXT:
查看context:GetThreadContext()
设置context:SetThreadContext()
l 线程的挂起和恢复:
ResumeThread ():将挂起计数减1,当挂起计数为0,唤醒线程。
(若调用成功,返回线程的前一个挂起计数;否则,返回0xFFFFFFFF)
SuspendThread():将挂起计数加1,挂起线程。
Sleep():线程睡眠,让出时间来调度其他线程。
(
假如需要强制CPU停止调度当前线程,把时间片让给其他线程,可以使用Sleep(0)或SwitchToThread(),两者的区别如下:
Sleep():时间片只能让给优先级相同或更高的线程;
SwitchToThread():只要有可调度线程,即便优先级较低,也会让其调度。
)
l 两个从消息队列取消息的函数:
PeekMessage():从消息队列取一条消息,但不取走。
GetMessage():从消息队列取一条消息,并取走。
l 线程优先级:
优先级为0的线程:系统启动时,会创建一个优先级为0的“页面清零线程”,它只有在系统中没有其他可调度线程时,才能调度,用来清除内存中的闲置页面。
优先级在1 ~ 15之间的线程:一般用户模式下,线程的优先级都在该范围。
优先级在16 ~ 30之间的线程:一般是内核线程。
线程的抢占:
较高优先级的线程,总是会抢占较低优先级线程的处理时间。一般而言,较高优先级的线程大多数都是不可调度的,否则会长期占用CPU时间。
动态提升优先级:
如上文所述,线程都有一个基本优先级,其范围从0到31。线程的优先级一般保持在基本优先级附近,但不是固定的,因为系统可能在某些情况下暂时将该优先级提高。
例如:当系统检测到有线程处于饥饿状态3到4秒时,会暂时将该线程的优先级提升到15,并允许其执行两个时间片。当两个时间片执行完,会将其恢复到基本优先级。
(注意:系统只会提升优先级在1 ~ 15之间的线程,而且提升后优先级不会高于15,以防影响操作系统。同时,系统只能提升一个线程的优先级,而不能降低其优先级)
相关函数:
SetProcessPriorityBoost( …):是否允许对进程进行优先级提升;
SetThreadPriorityBoost( …):是否允许对线程进行优先级提升;
GetProcessPriorityBoost( …):当前是否启用进程优先级提升;
GetThreadPriorityBoost( …):当前是否启用线程优先级提升。
l 为线程指定CPU:
NUMA:
非统一内存访问计算机体系结构。由多个系统板组成,每个系统板都有自己的CPU和内存块。每个CPU可访问任何一块系统板的内存,但访问自己所在系统板的内存比较快,而访问其它板上内存特别慢。
为线程指定CPU的好处:
如上介绍的NUMA系统,应该指定一个进程中所有的线程运行在某块系统板上,这样才能保证该板上的CPU在调度这些线程时不会访问其它板上的内存,从而保证效率。此外,假如为线程分配固定的CPU,可以减少CPU时间片切换带来的时间开销,这在某些情况下也是有利于提高工作效率的。
相关函数:
SetProcessAffinityMask(HANDLE hProcess, DWORD_PTR dwProcessAffinityMask):
(1)hProcess:要设置的进程。
(2)dwProcessAffinityMask:位掩码,表示可以在哪些CPU上运行。该参数每个二进制位表示一个CPU的状态,若某位为1,表示能在该位对应的CPU上运行本进程(例如,0x5表示可以在CUP0和CPU2上运行)。
GetProcessAffinityMask( …):返回进程关联性掩码。
SetThreadAffinityMask( …):设置线程关联性掩码。(用该函数指定CPU,则在线程内部不能使用sleep,否则会被其他CPU抢占资源,没能完全起到指定CPU的目的)
SetThreadIdealProcessor( …):设置线程的理想CPU(该函数第二个参数是0 ~ 31/63之间的数,表示理想CPU的编号)。用该函数设定了理想CPU,那么系统会优先选择该CPU来运行线程,同时当该CPU繁忙时,允许让线程在其它空闲CPU上运行,这样做效率更高。