【文起】亲爱的,已经12月份了,相聚的日子已经很临近临近了。爱你蟹儿,加油
线程的调度、优先级以及亲缘性
1、 系统只调度可以调度的线程。如果线程的暂停次数大于1,说明该线程已经暂停运行,不会给其安排任何CPU时间。调用含有CREATE_SUSPENDED的CreateProcess或CreateThread函数,都可以创建一个暂停的线程,同时也可以使用SuspendThread和ResumeThread函数
2、 恢复、暂停线程:
线程内核对象中有一个值,用于指明线程的暂停次数。调用CreateProcess或CreateThread时,创建线程内核对象,暂停计数被赋值为1.(防止还没初始化还就被CPU调度了),线程完全初始化之后,CreateProcess和CreateThread函数要查看是否传递了CREATE_SUSPENDED标志,如果传递了直接返回,否则将暂停计数递减为0。这是线程就是可调度的线程了。
DWORD SuspendThread(HANDLE hThread);
DWORD ResumeThread(HANDLE hThread);
均返回当前暂停计数。其中SuspendThread使用要注意,该函数与内核方式的执行是异步进行的,因为不知道当前线程到底在干什么就暂停,会导致死锁。
3、 恢复、暂停进程
由于经常从来不会被安排获得CPU时间。但是进程中有很多线程,如何暂停或者恢复进程中所有的线程呢?Windows只有在调试程序时,可以通过调用WaitForDebugEvent和
ContinueDebugEvent函数来进行。
我们可以通过之前知识,写一个暂停或者恢复进程中所有线程的方法,不过这个方法有缺陷,因为快照出一个进程中所有线程后,无法保证没有新的线程生成或者旧的线程结束。
VOID SuspendProcess(DWORD dwProcessID,BOOL fSuspend)
{
//取出进程快照
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,dwProcessID);
if (INVALID_HANDLE_VALUE == hSnapshot)
{
return;
}
//获取线程信息
THREADENTRY32 thread32 = {sizeof(thread32)};
BOOL bmore = Thread32First(hSnapshot,&thread32);
while(bmore)
{
//找到对应的线程,那么就进入操作下
if (dwProcessID == thread32.th32OwnerProcessID)
{
HANDLE hTread = OpenThread(THREAD_SUSPEND_RESUME,FALSE,thread32.th32ThreadID);
if (NULL != hTread)
{
if (fSuspend)
{
printf("暂停线程\r\n");
SuspendThread(hTread);
}
else
{
printf("恢复线程\r\n");
ResumeThread(hTread);
}
}
CloseHandle(hTread);
}
bmore = Thread32Next(hSnapshot,&thread32);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
//不能用GetCurrentProcessId()去当前进程来玩哦,不然程序无法继续了。
STARTUPINFO si = {sizeof(si)};
PROCESS_INFORMATION pi;
TCHAR szline[] = _T("calc.exe");
CreateProcess(NULL,szline,NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&si,&pi);
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
//暂停进程中所有线程
SuspendProcess(pi.dwProcessId,TRUE);
Sleep(1000);
//恢复进程中所有线程
SuspendProcess(pi.dwProcessId,FALSE);
return 0;
程序需要包含的头文件是
#include <Windows.h>
#include <TlHelp32.h>
运行程序,可以看到如下结果:
暂停线程
恢复线程
请按任意键继续. . .
4、Sleep()函数
a、调用该函数,使线程自愿放弃它剩余的时间片;
b、由于Windows不是实时系统,所以系统会在大约的毫秒数内使线程不可调度;
c、如果参数dwMilliseconds传递为INFINITE,就是告诉系统永远都不要调度该线程;
d、如果参数dwMilliseconds传递为0,那么告诉系统,调用线程将释放剩余的时间片,并迫使系统调度另一个线程。
5、转换到另一个线程
BOOL SwitchToThread()
调用这个函数时,系统会查看下是否有一个迫切需要CPU的线程,如果没有就会返回FALSE,如果存在,那么SwitchToThread就会对该线程进行调度。
6、线程运行的时间
我们的系统是抢占式的,所以线程运行时可能会被其他线程打断,这样通过Tick时间相减得出的结果不一定正确。
操作系统提供了GetThreadTimes函数用来获取时间(同样的GetProcessTimes来获取进程运行时间信息)。 如下代码为举例:
__int64 FileTimeToQuadWord(FILETIME *pft)
{
return(Int64ShllMod32(pft->dwHighDateTime,32)|pft->dwLowDateTime);
}
int _tmain(int argc, _TCHAR* argv[])
{
FILETIME ftKernelTimeStart,ftKernelTimeEnd;
FILETIME ftUserTimeStart,ftUserTimeEnd;
FILETIME ftDummy;
__int64 qwKernelTimeElapsed,qwUserTimeElapsed,qwTotalTimeElapsed;
GetThreadTimes(GetCurrentThread(),&ftDummy,&ftDummy,&ftKernelTimeStart,&ftUserTimeStart);
//不能用GetCurrentProcessId()去当前进程来玩哦,不然程序无法继续了。
STARTUPINFO si = {sizeof(si)};
PROCESS_INFORMATION pi;
TCHAR szline[] = _T("calc.exe");
CreateProcess(NULL,szline,NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&si,&pi);
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
//暂停进程中所有线程
SuspendProcess(pi.dwProcessId,TRUE);
Sleep(1000);
//恢复进程中所有线程
SuspendProcess(pi.dwProcessId,FALSE);
GetThreadTimes(GetCurrentThread(),&ftDummy,&ftDummy,&ftKernelTimeEnd,&ftUserTimeEnd);
qwKernelTimeElapsed = FileTimeToQuadWord(&ftKernelTimeEnd) -
FileTimeToQuadWord(&ftKernelTimeStart);
qwUserTimeElapsed = FileTimeToQuadWord(&ftUserTimeEnd) -
FileTimeToQuadWord(&ftUserTimeStart);
qwTotalTimeElapsed = qwKernelTimeElapsed + qwUserTimeElapsed;
printf("Total time is %u",qwTotalTimeElapsed);
return 0;
}
7、查看CONTEXT结构:
可以调用GetThreadContex函数获取当前进程的CONTEXT,也可以使用SetThreadContext修改内核对象。不过调用这两个函数时,记得都必须想SuspendThread,不然结果无法预测
8、线程优先级
可以通过调用SetPriorityClass来改变自己的优先级,也可以通过GetPriorityClass来检索进程的优先级。
9、亲缘性:
当系统将线程分配给处理器时,Windows将根据亲缘性进行操作,如果有其他因素相同,那么将设法在上次运行的那个处理器上运行。
也就是说,我们可以通过函数,设置线程在哪个CPU上运行。如函数SetThreadIdealProcessor
【文尾】如果文章对您有帮助,请留下对我和蟹儿的祝福。谢谢~~