首页 > 代码库 > Windows核心编程笔记(5)----线程调度,优先级

Windows核心编程笔记(5)----线程调度,优先级

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);



Windows核心编程笔记(5)----线程调度,优先级