首先线程与系统时钟间隔参见http://blog.csdn.net/wangpengk7788/article/details/54287947
在抢占式多任务操作系统中,线程的运行是有限制的,系统会调度一个线程在一个时间块内占用CPU,在时间到了之后将线程的上下文(CONTEXT结构,保存线程切换前的CPU个寄存器的值)保存到线程内核对象中,从另一个可调度线程的CONTEXT中获取属于它的CPU各寄存器的值,设置CPU寄存器,执行该线程,这样重复着在不同的线程之间切换,保证每个可调度线程能得到执行时间。
在我使用的64位WIN7中 GetSystemTimeAdjustment
DWORD SuspendThread(HANDLE hThread ); 返回线程上次挂起的次数
挂起一个线程,一个线程最多可以挂起, 一个线程最多可以挂起MAXIMUM_SUSPEND_COUNT (127)次, 被挂起的线程不参与线程调度
DWORD ResumeThread(HANDLE hThread ); 返回上次挂起次数
递减线程内核对象中 线程挂起计数,只有在挂起减到0时,线程才能恢复运行
进程不能挂起,除了在调试器处理WaitForDebugEvent时,被调试的进程所有线程会挂起。
Sleep函数
1、调用Sleep函数使线程挂起,线程放弃剩余的时间片
2、Sleep设置的挂起时间只是近似值,不保证会准时醒来
3、可以传入INFINITE线程永久挂起
4、传入0表示放弃当前使用的剩余时间片
SwitchToThread 让出时间片,可能调度给比自己优先级低的线程
可以在一段代码的之前使用GetThreadTime获取线程执行的时间,取前后时间的差 做为该段代码的执行时耗。
在VISTA中为线程分配CPU时间的方式发生了变化 ,在新的系统中使用处理器时间戳计时器TSC,在线程被调度程序暂停时,将计算此时TSC的值与线程开始时的TSC值之间的差,然后在线程执行时间上加上该差值,不计算中断时间。我们可以使用ReadTimeStampCounter宏来获取TSC的值。
GetThreadContext 获取线程的上下文,这里CONTEXT结构对熟悉汇编的看起来会很亲切。Windwos 还是提供了SetThreadContext函数,这是很强大的对于喜欢做一些比较另类和猥琐的事情的人,比如可以创建一个挂起的进程比如系统的计算器,把另一个执行文件,比如系统的记事本程序自己加载到这个挂起的进程空间中去,然后改变主线程的CONTEXT,设置成记事本程序主线程入口所需要的环境,那么就可以借尸还魂了。在任务管理器里看到的是一个计算器程序,实际运行的代码是记事本的。
线程的优先级
在Windows中线程的优先级为0-31,优先级高的线程优先被调度,只有在优先级高的线程都处于不可调度状态时,才递减调度其次优先级的线程,当较高优先级的线程占用了CPU时间,致使较低优先级的线程无法运行时成为饥饿,在多处理器上饥饿的情况发生的可能性很小,系统大多数线程都是不可调度的,例如线程调用了GetMessage等待获取消息,如果这时消息队列中没有消息,系统会暂停这个线程,并将CPU分配给另一个等待调度的线程。
在较低优先级的线程被调度时,如果有较高优先级的线程已经处于可调度状态,系统会暂停较低优先级的线程,将CPU分给高优先级的线程,Windows的线程调度是抢占掠夺式的。
Windwos的进程优先级分为6个等级,可以通过SetPriorityClass来设置
BOOL WINAPI SetPriorityClass(
__in HANDLE hProcess,
__in DWORD dwPriorityClass
);
dwPriorityClass对应的标志 实际优先级值
优先级 | 标志 | 优先级值 |
idle (低) | IDLE_PRIORITY_CLASS | 4 |
Below (低于标准) | BELOW_NORMAL_PRIORITY_CLASS | |
normal (标准) | NORMAL_PRIORITY_CLASS | 7或9 |
Above (高于标准) | ABOVE_NORMAL_PRIORITY_CLASS | |
high (高) | HIGH_PRIORITY_CLASS | 13 |
realtime (实时) | REALTIME_PRIORITY_CLASS | 24 |
Windwos的线程有6个相对于进程的优先级,相对优先级使用 SetThreadPriority来设置
BOOL WINAPI SetThreadPriority(
__in HANDLE hThread,
__in int nPriority
);
nPriority对应的标志 在进程优先级的基础上调整
线程优先级等级 | 标志 | 优先级值 |
idle (最低) | THREAD_PRIORITY_IDLE | 如果进程优先级为realtime则调整为16,其它情况为1 |
LOWEST 低 | THREAD_PRIORITY_LOWEST | -2(在原有基础上-2) |
BELOW 低于标准 | THREAD_PRIORITY_BELOW_NORMAL | -1(在原有基础上-1) |
NORMAL(标准) | THREAD_PRIORITY_NORMAL | 不变(取进程优先级值) |
ABOVE 高于标准 | THREAD_PRIORITY_ABOVE_NORMAL | +1(在原有基础上+1) |
HIGHEST(高) | THREAD_PRIORITY_HIGHEST | +2(在原有基础上+2) |
CRITICAL(最高) | THREAD_PRIORITY_TIME_CRITICAL | 如果进程优先级为realtime则调整为31,其它情况为15 |
这些进程优先级和线程相对优先级在Windwos不同版本 所对应的优先级值得映射不有变化的。
当一个进程中的线程为了响应某种IO事件,比如窗口消息,磁盘读取,获取网络包,线程会在这时被系统动态提升为NORMAL级,且只能提升1~15级的线程。在线程提升优先级后的每个时间片内优先级会被递减,直到恢复原来的优先级。
可以使用SetProcessPriority 来设置是否允许系统动态提升线程优先级。使用GetProcessPriority来获取线程是否开启被提升。
调度IO请求优先级
在设置进程和线程优先级时可以为 这两个函数传入 对应的THREAD_MODE_BACKGROUND_BEGIN PROCESS_MODE_BACKGROUND_BEGIN 让线程或进程进入低IO优先级状态,传入THREAD_MODE_BACKGROUND_END PROCESS_MODE_BACKGROUND_END 进入NORMAL级状态。
CPU关联性
我们可以使用下满两个函数设置线程或进程运行于哪个CPU之上。dwProcessAffinityMask 用二进制位做掩码,比如3 代码运行在CPU0和CPU1上,5表示在CPU2和CPU0上
BOOL WINAPI SetProcessAffinityMask(
__in HANDLE hProcess,
__in DWORD_PTR dwProcessAffinityMask
);
DWORD_PTR WINAPI SetThreadAffinityMask(
__in HANDLE hThread,
__in DWORD_PTR dwThreadAffinityMask
);