1、操作系统线程调度过程
每个线程都有一个上下文CONTEXT结构体,保存在线程的内核对象中,这个上下文中保存了线程上一次执行时CPU寄存器
的状态。每隔固定时间,Windows会查看所有当前存在的线程内核对象,其中只有一些是可调度的。Windows在可调度的
线程中选择一个,并将上次保存到线程上下文中的数据载入CPU寄存器中。(上下文切换)
CPU时间片到后,Windows移出这个线程,把CPU寄存器信息保存到线程上下文中,切换到另一个线程,如此循环。
2、线程的挂起和恢复
调用CreateProcess或者CreateThread时,系统将创建线程内核对象,并将挂起计数初始化为1,这样系统就不会给这个线
程调度CPU了。在线程初始化完成后,函数将查看是否有CREATE_SUSPENDED标志传入。如果有,函数返回并让新的线程处于
挂起状态;没有,函数会将线程的挂起计数递减为0,挂起计数为0时,线程就成为可调度的了。
使用ResumeThread函数恢复线程至可调度状态,该函数返回线程的前一个挂起计数,失败返回0xffffffff。
SuspendThread挂起线程,一个线程最多可以挂起127次。SuspendThread是异步的,只有在确切知道线程正在做什么,而且
采用完备措施避免出现因挂起线程而引起的问题或者死锁时,调用SuspendThread才是安全的。
3、睡眠
Sleep函数告诉系统线程在多少时间内不需要调度,线程自动放弃CPU时间片剩余时间。由于Windows并不是实时操作系统
睡眠时间不一定准。
Sleep(0)则是告诉系统,当前线程主动放弃CPU时间片剩余时间,强制系统调用其他线程。如果没有相同或者优先级更高的
可调度线程,系统会重新调度这个线程。
4、切换到另一个线程
SwitchToThread切换线程,系统查看是否有正急需CPU的线程。如果没有,函数立即返回;如果存在,SwitchToThread将
调度该线程,继续运行的线程可以执行一个CPU时间片,然后系统调度恢复原线程。
SwitchToThread与Sleep(0)
SwitchToThread允许执行低优先级的线程,Sleep(0)则是即使有优先级低的饥饿线程也不会被调度。
5、线程上下文结构CONTEXT
系统使用CONTEXT记录线程的状态,这样在线程下次执行时,就可以从上下文停止处继续执行。
在Windows定义的所有结构体中,CONTEXT是唯一一个特定于CPU的。
可以使用GetThreadContext获取线程内核的上下文,使用前需要先挂起线程,否则获取的CONTEXT信息就与当前运行线程的
上下文不一致了。一个线程有两个上下文模式:用户模式和内核模式,GetThreadContext获取的是用户模式的上下文信息。
6、
较高优先级的线程总会抢占较低优先级的线程,无论低优先级线程是否正在运行。系统启动时,将创建一个页面清零线程(
zero page thread),这个线程的优先级为0。页面清零线程负责在没有其他线程需要执行时,将系统内存中的所有闲置页
面清零。
7、调度I\O请求优先级
如果一个低优先级的线程获得CPU时间,可以很轻易在短时间内将成百上千个I\O请求入列,I\O请求一般需要时间处理,因
此低优先级线程可能会挂起高优先级线程,使它们无法完成任务,从而显著影响系统的响应性。
可以通过SetThreadPriority传入THREAD_MODE_BACKGROUND_BEGIN属性,告诉Windows线程应该发送低优先级的I\O请求,这
也将降低线程的CPU调度优先级。通过设置THREAD_MODE_BACKGROUND_END属性,可以恢复线程的优先级。
系统不允许线程改变另一个线程的I\O请求优先级。
同样的,可以通过SetPriorityClass传入PROCESS_MODE_BACKGROUND_BEGIN属性,设置进程内所有线程进行低优先级的I\O
请求和CPU调度。PROCESS_MODE_BACKGROUND_END属性来恢复。
系统不允许进程改变另一个进程的I\O请求优先级。
测试代码
挂起一个进程里的所有线程,使用需注意(可能在遍历后有新的线程创建、销毁)
void SuspendProcess(DWORD dwPid, BOOL bSuspend)
{
HANDLE hSnapshort = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwPid);
if ( INVALID_HANDLE_VALUE != hSnapshort )
{
THREADENTRY32 te = { sizeof(THREADENTRY32) };
BOOL bRet = Thread32First(hSnapshort, &te);
while( bRet )
{
if ( te.th32OwnerProcessID == dwPid )
{
HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, te.th32ThreadID);
if ( NULL == hThread )
continue;
if ( bSuspend )
SuspendThread(hThread);
else
ResumeThread(hThread);
CloseHandle(hThread);
}
bRet = Thread32Next(hSnapshort, &te);
}
CloseHandle(hSnapshort);
}
}
设置线程优先级,获取线程上下文信息
HANDLE hThread = CreateThread(NULL, 0, TestThread, 0, CREATE_SUSPENDED, NULL);
SetThreadPriority(hThread, THREAD_PRIORITY_IDLE|THREAD_MODE_BACKGROUND_BEGIN);
ResumeThread(hThread);
CloseHandle(hThread);
SuspendThread(hThread);
SuspendThread(hThread);
DWORD dwCount = ResumeThread(hThread);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
SuspendProcess(316, TRUE);
SuspendProcess(316, FALSE);
CONTEXT context;
memset(&context, 0, sizeof(CONTEXT));
context.ContextFlags = CONTEXT_INTEGER;//CONTEXT_CONTROL;
SuspendThread(GetCurrentThread());
GetThreadContext(GetCurrentThread(), &context);