线程的调度、优先级和亲缘性——Windows核心编程学习手札系列之七
線(xiàn)程的調(diào)度、優(yōu)先級(jí)和親緣性
——Windows核心編程學(xué)習(xí)手札系列之七
每個(gè)線(xiàn)程都擁有一個(gè)上下文結(jié)構(gòu),在線(xiàn)程的內(nèi)核對(duì)象中,記錄線(xiàn)程上次運(yùn)行時(shí)該線(xiàn)程的CPU寄存器狀態(tài)。Windows會(huì)每隔20ms左右查看當(dāng)前存在的所有線(xiàn)程內(nèi)核對(duì)象,在這些對(duì)象中,選擇可調(diào)度的對(duì)象,將其上下文結(jié)構(gòu)(內(nèi)核對(duì)象中)加載到CPU的寄存器中,其值就是上次保存在線(xiàn)程環(huán)境中的值,此為上下文切換。Windows保存了一個(gè)記錄,說(shuō)明每個(gè)線(xiàn)程獲得調(diào)度的機(jī)會(huì),Microsoft的Spy++工具,可以查看這個(gè)。操作系統(tǒng)只調(diào)度可以調(diào)度的線(xiàn)程,實(shí)際中,大多數(shù)線(xiàn)程是不可調(diào)度的,如暫停的線(xiàn)程(CREATE_SUSPENDED標(biāo)志)以及等待事件觸發(fā)的線(xiàn)程等。
線(xiàn)程內(nèi)核對(duì)象的內(nèi)部有線(xiàn)程的暫停計(jì)數(shù)值,當(dāng)調(diào)用CreateProcess或CreateThread函數(shù)時(shí),就創(chuàng)建了線(xiàn)程的內(nèi)核對(duì)象,且它的暫停計(jì)數(shù)被初始化為1,防止線(xiàn)程被調(diào)度到CPU中,因?yàn)榫€(xiàn)程初始化需要時(shí)間,需要在準(zhǔn)備好開(kāi)始執(zhí)行線(xiàn)程。當(dāng)線(xiàn)程完全初始化后,CreateProcess或CreateThread要查看是否傳遞了CREATE_SUSPENDED標(biāo)志,如果傳遞了給標(biāo)志,那函數(shù)返回,新線(xiàn)程處于暫停狀態(tài);如未傳遞該標(biāo)志,那函數(shù)將線(xiàn)程的暫停計(jì)數(shù)遞減為0,此時(shí)如果線(xiàn)程沒(méi)有等待事件,那么該線(xiàn)程處在可調(diào)度的狀態(tài)。在暫停狀態(tài)中創(chuàng)建一個(gè)線(xiàn)程,就可以在線(xiàn)程有機(jī)會(huì)執(zhí)行前改變線(xiàn)程的運(yùn)行環(huán)境(如優(yōu)先級(jí))。要恢復(fù)線(xiàn)程的可調(diào)度性,可調(diào)用函數(shù)ResumeThread,將調(diào)用CreateThread函數(shù)時(shí)返回的線(xiàn)程句柄傳遞給它。DWORD ResumeThread(HANDLE hThread)運(yùn)行成功將返回線(xiàn)程的前一個(gè)暫停計(jì)數(shù),否則返回0xFFFFFFFF值,單個(gè)線(xiàn)程可以暫停若干次,如一個(gè)線(xiàn)程序暫停了3次需要恢復(fù)3次才可以被分配給一個(gè)CPU。創(chuàng)建線(xiàn)程,除傳遞CREATE_SUSPENDED標(biāo)志外,還可以調(diào)用DWORD SuspendThread(HANDLE hThread)函數(shù)來(lái)暫停線(xiàn)程的運(yùn)行。任何線(xiàn)程都可以調(diào)用該函數(shù)來(lái)暫停另一個(gè)線(xiàn)程的運(yùn)行(只要有線(xiàn)程的句柄),線(xiàn)程可以自行暫停運(yùn)行,但不能自行恢復(fù)運(yùn)行。與ResumeThread一樣,SuspendThread返回的是線(xiàn)程的一個(gè)暫停計(jì)數(shù),暫停計(jì)數(shù)最多是MAXIMUM_SUSPEND_COUNT次(在WinNT.h中定義為127),SuspendThread與內(nèi)核方式是異步運(yùn)行的,但在線(xiàn)程恢復(fù)運(yùn)行之前,不會(huì)發(fā)生用戶(hù)方式的執(zhí)行。使用SuspendThread函數(shù)暫停線(xiàn)程應(yīng)該在確切知道目標(biāo)線(xiàn)程正在做什么情況下,并采取措施避免因暫停線(xiàn)程的運(yùn)行而帶來(lái)的問(wèn)題或死鎖,因?yàn)槿绻€(xiàn)程正試圖從堆棧中分配內(nèi)存,該線(xiàn)程會(huì)在該堆棧上設(shè)置鎖,當(dāng)其他線(xiàn)程訪(fǎng)問(wèn)該堆棧時(shí)將被停止,直到暫停線(xiàn)程的恢復(fù)。
Windows中不存在暫停或恢復(fù)進(jìn)程的概念,允許一個(gè)進(jìn)程暫停另一個(gè)進(jìn)程中所有線(xiàn)程的運(yùn)行,但從事暫停操作的進(jìn)程必須是個(gè)調(diào)試程序,也需要調(diào)用WaitForDebugEvent和ContinueDebugEvent之類(lèi)的函數(shù)。總言,Windows沒(méi)有提供方法暫停進(jìn)程中的所有線(xiàn)程運(yùn)行。這里有一個(gè)作者寫(xiě)的暫停進(jìn)程函數(shù)作為要暫停進(jìn)程的參考用。
Void SuspendProcess(DWORD dwProcessID,BOOL fSuspend){
?????? //get the list of threads in the system
?????? HANDLE hSnapshot=CreateToolhelp32Snapshot(
?????????????????????????????????????????????????????????????? TH32CS_SNAPTHREAD,dwProcessID);
?????? if(hSnapshot != INVALID_HANDLE_VALUE){
????????????? //walk the list of threads
????????????? THREADENTRY32 te={sizeof(te)};
????????????? BOOL fOk=Thread32First(hSnapshot,&te);
????????????? for(;fOk;fOk=Thread32Next(hSnapshot,&te)){
???????????????????? //Is this thread in the desired process?
???????????????????? if(te.th32OwnerProcessID = = dwProcessID){
??????????????????????????? //attempt to convert the thread ID into a handle
??????????????????????????? HANLDE hThread=OpenThread(THREAD_SUSPEND_RESUME,FALSE,
??????????????????????????????????????????????????????????????????????????????????? te.th32ThreadID);
??????????????????????????? if(hThread!=NULL){
?????????????????????????????????? //suspend or resume the thread
?????????????????????????????????? if(fSuspend)
????????????????????????????????????????? SuspendThread(hThread);
?????????????????????????????????? Else
????????????????????????????????????????? ResumeThread(hThread);
??????????????????????????? }
??????????????????????????? CloseHandle(hThread);
???????????????????? }
????????????? }
????????????? CloseHandle(hSnapShot);
?????? }
}
該函數(shù)使用ToolHelp函數(shù)枚舉系統(tǒng)中的線(xiàn)程列表,當(dāng)找到作為指定進(jìn)程的組成部分的線(xiàn)程時(shí),就調(diào)用HANDLE OpenThread(DWORD dwDesiredAccess,BOOL bInheritHandle,DWORD dwThreadID)函數(shù)找出匹配線(xiàn)程ID的線(xiàn)程內(nèi)核對(duì)象,對(duì)內(nèi)核對(duì)象的使用計(jì)數(shù)進(jìn)行遞增,然后返回對(duì)象的句柄,運(yùn)用這個(gè)句柄可調(diào)用SuspendThread或ResumeThread來(lái)暫停或恢復(fù)線(xiàn)程的運(yùn)行。
如果線(xiàn)程不想愛(ài)某個(gè)時(shí)間段被調(diào)度,可以調(diào)用Sleep函數(shù)來(lái)實(shí)現(xiàn):void sleep(DWORD dwMillisecondes),這個(gè)函數(shù)使線(xiàn)程暫停自己運(yùn)行,知道dwMillisecondes后,該函數(shù)需要注意的是:1)調(diào)用sleep函數(shù),使線(xiàn)程自愿放棄它的剩余時(shí)間片;2)系統(tǒng)將在大約的指定毫秒內(nèi)使線(xiàn)程不可調(diào)度;3)可為sleep函數(shù)的參數(shù)dwMillisecondes傳遞INFINITE,告訴系統(tǒng)永不調(diào)度該線(xiàn)程,這不提倡,最好讓線(xiàn)程退出,還原其堆棧和內(nèi)核對(duì)象;4)可將0傳遞給sleep,這樣調(diào)用線(xiàn)程將釋放剩余時(shí)間片,并迫使系統(tǒng)調(diào)度另一個(gè)線(xiàn)程。
函數(shù)BOOL SwitchToThread()可轉(zhuǎn)換到另一個(gè)線(xiàn)程。當(dāng)調(diào)用這個(gè)函數(shù)時(shí),系統(tǒng)查看是否存在一個(gè)迫切需要CPU時(shí)間的線(xiàn)程,如沒(méi)有,SwitchToThread就會(huì)立即返回,如果存在,SwitchToThread就對(duì)該線(xiàn)程進(jìn)行調(diào)度。這個(gè)函數(shù)允許一個(gè)需要資源的線(xiàn)程強(qiáng)制另一個(gè)優(yōu)先級(jí)較低、而目前卻擁有該資源的線(xiàn)程放棄該資源。如果調(diào)用SwitchToThread函數(shù)時(shí)沒(méi)有其他線(xiàn)程能夠運(yùn)行,則返回FALSE,否則返回非0值。與sleep函數(shù)相似,區(qū)別在于SwitchToThread允許優(yōu)先級(jí)較低的線(xiàn)程運(yùn)行,即使低優(yōu)先級(jí)線(xiàn)程迫切需要CPU時(shí)間,sleep也能夠立即對(duì)調(diào)用線(xiàn)程重新進(jìn)行調(diào)度。
要獲取線(xiàn)程的運(yùn)行時(shí)間需要調(diào)用GetThreadTimes函數(shù),這個(gè)返回線(xiàn)程得到的CPU時(shí)間數(shù)量。具體實(shí)現(xiàn)代碼如下:
#include <windows.h>
?
__int64 FileTimeToQuadWord(PFILETIME pft)
{
?????? return(Int64ShllMod32(pft->dwHighDateTime,32) | pft->dwLowDateTime);
}
?
int main(int argc, char* argv[])
{
?????? FILETIME ftKernelTimeStart,ftKernelTimeEnd;
?????? FILETIME ftUserTimeStart,ftUserTimeEnd;
?????? FILETIME ftDummy;
?????? __int64 qwKernelTimeElapsed,qwUserTimeElapsed,qwTotalTimeElapsed;
?
?????? //Get start times
?????? GetThreadTimes(GetCurrentThread(),&ftDummy,&ftDummy,&ftKernelTimeStart,&ftUserTimeStart);
?
?????? //perform complex algorithm here.
?????? //Get ending times.
?????? GetThreadTimes(GetCurrentThread(),&ftDummy,&ftDummy,&ftUserTimeEnd,&ftUserTimeEnd);
?
?????? //Get the elapsed kernel and user times by converting the start and end times form FILETIMEs
?????? //to quad words and then subtract the start times from the end times.
?????? qwKernelTimeElapsed=FileTimeToQuadWord(&ftKernelTimeEnd)-FileTimeToQuadWord(&ftKernelTimeStart);
?
?????? qwUserTimeElapsed=FileTimeToQuadWord(&ftUserTimeEnd)-FileTimeToQuadWord(&ftUserTimeStart);
?
?????? //Get total time duration by adding the kernel and user times.
?????? qwTotalTimeElapsed=qwKernelTimeElapsed+qwUserTimeElapsed;
?
?????? // the total elapsed time is in qwTotalTimeElapsed and display in console
?????? printf("The executing times of thread is %d /n",qwTotalTimeElapsed);
?????? //printf("Hello World!/n");
?????? return 0;
}
環(huán)境結(jié)構(gòu)使系統(tǒng)保留線(xiàn)程的狀態(tài),在下次線(xiàn)程擁有CPU時(shí),能夠回到上次中斷運(yùn)行的地方。Windows允許查看線(xiàn)程內(nèi)核對(duì)象的內(nèi)部情況,以便抓取它當(dāng)前的一組CPU寄存器,若要執(zhí)行該項(xiàng)操作,可調(diào)用GetThreadContext函數(shù):
BOOL GetThreadContext(HANDLE hThread,PCONTEXT pContext);
調(diào)用該函數(shù),只需指定一個(gè)CONTEXT結(jié)構(gòu),對(duì)某些標(biāo)志(該結(jié)構(gòu)中的ContextFlags成員)進(jìn)行初始化,指明想要收回那些寄存器,并將該結(jié)構(gòu)的地址傳遞給函數(shù),函數(shù)會(huì)將數(shù)據(jù)填入到所要求的成員中。在調(diào)用GetThreadContext函數(shù)前,應(yīng)調(diào)用SuspendThread,否則線(xiàn)程可能被調(diào)度,且線(xiàn)程的環(huán)境與所收回的不同。一個(gè)線(xiàn)程實(shí)際有兩個(gè)環(huán)境,一個(gè)是用戶(hù)方式,一個(gè)是內(nèi)核方式。GetThreadContext只能返回線(xiàn)程的用戶(hù)方式環(huán)境,如調(diào)用SuspendThread來(lái)停止線(xiàn)程的運(yùn)行,但該線(xiàn)程目前正運(yùn)行在內(nèi)核方式下,那么即使SuspendThread尚未暫停該線(xiàn)程的運(yùn)行,它的用戶(hù)方式仍然處于穩(wěn)定狀態(tài)。線(xiàn)程在恢復(fù)用戶(hù)方式之前,無(wú)法執(zhí)行更多的用戶(hù)方式代碼,因此可放心將線(xiàn)程視為處于暫停狀態(tài),GetThreadContext函數(shù)將能正常運(yùn)行。CONTEXT結(jié)構(gòu)的ContextFlags成員并不與任何CPU寄存器對(duì)應(yīng)。無(wú)論是何種CPU結(jié)構(gòu),該成員存在于CONTEXT結(jié)構(gòu)定義中。ContextFlags成員用于向GetThreadContext函數(shù)指明想要檢索那些寄存器。如想獲得線(xiàn)程的控制寄存器,可以用如下代碼:
//Create a CONTEXT structure.
?????? CONTEXT Context;
?????? //Tell the system that we are interested in only the control registers.
?????? Context.ContextFlags=CONTEXT_CONTROL;
?????? //Tell the system to get the registers associated with a thread.
?????? GetThreadContext(hThread,&Context);
在調(diào)用GetThreadContext之前,須對(duì)CONTEXT結(jié)構(gòu)中的ContextFlags成員進(jìn)行初始化,如想獲得線(xiàn)程的控制寄存器和整數(shù)寄存器,需要進(jìn)行下面的ContextFlags初始化:
Context.ContextFlags=CONTEXT_CONTROL | CONTEXT_INTEGER;
也可以獲得線(xiàn)程的所有重要的寄存器(Mcirosoft認(rèn)為最常用的寄存器):
Context.ContextFlags=CONTEXT_FULL;
當(dāng)GetThreadContext返回時(shí),可容易查看線(xiàn)程的任何寄存器值,要編寫(xiě)與CPU相關(guān)的代碼。Windows可修改CONTEXT結(jié)構(gòu)中的成員,然后通過(guò)SetThreadContext將新寄存器值放回線(xiàn)程的內(nèi)核對(duì)象中:
BOOL SetThreadContext(HANDLE hThread,CONST CONTEXT *pContext);
修改其環(huán)境的線(xiàn)程前應(yīng)先暫停線(xiàn)程,否則結(jié)果不得而知。下面的代碼是演示:
//Create a CONTEXT structure.
?????? CONTEXT Context;
?????? //stop the thead from running
?????? SuspendThread(hThread);
??????
?????? //Get the thread's control registers.
?????? Context.ContextFlags=CONTEXT_CONTROL;
?????? //Tell the system to get the registers associated with a thread.
?????? GetThreadContext(hThread,&Context);
?
?????? //Make the instruction pointer point to the address of your choice.
?????? //Here I've arbitrarily set the address instruction pointer to 0x00010000
#if defined(_ALPHA_)
?????? Context.Fir=0x00010000;
#elif defined(_X86_)
?????? Context.Eip=0x00010000;
#else
#error Module contains CPU-specific code;modify and recompile.
#endif
?
?????? //Set the thread's registers to reflect the changed values.
?????? //It's not really necessary to reset the ControlFlags memeber because it was set earlier.
?????? Context.ContextFlags=CONTEXT_CONTROL;
?????? SetThreadContext(hThread,&Context);
?
?????? //Resuming the thread will cause it to begin execution at address 0x00010000.
?????? RusumeThread(hThread);
如此處理,可能導(dǎo)致遠(yuǎn)程線(xiàn)程中的違規(guī),向用戶(hù)顯示未處理的異常消息框,同時(shí)遠(yuǎn)程進(jìn)程終止運(yùn)行。GetThreadContext和SetThreadContext函數(shù)可以對(duì)線(xiàn)程進(jìn)行多方面控制,但要慎用。
線(xiàn)程被賦予不同的優(yōu)先級(jí),決定系統(tǒng)調(diào)度程序選擇調(diào)度哪個(gè)線(xiàn)程來(lái)運(yùn)行(使其擁有CPU)。每個(gè)線(xiàn)程都被賦予一個(gè)從0(最低)到31(最高)的優(yōu)先級(jí)號(hào)碼。當(dāng)系統(tǒng)引導(dǎo)時(shí),會(huì)創(chuàng)建一個(gè)特殊的線(xiàn)程,稱(chēng)為0頁(yè)線(xiàn)程,該線(xiàn)程被賦予優(yōu)先級(jí)為0,是整個(gè)系統(tǒng)中唯一的一個(gè)在優(yōu)先級(jí)0上運(yùn)行的線(xiàn)程。當(dāng)系統(tǒng)中沒(méi)有任何需要執(zhí)行操作時(shí),0頁(yè)線(xiàn)程負(fù)責(zé)將系統(tǒng)中的所有空閑RAM頁(yè)面置0。Windows支持6個(gè)優(yōu)先級(jí)類(lèi):空閑、低于正常、正常、高于正常、高和實(shí)時(shí),一般程序都處在正常這個(gè)級(jí)別。Windows Explorer是在高優(yōu)先級(jí)上運(yùn)行的,大多數(shù)時(shí)間Explorer線(xiàn)程是暫停的,等待用戶(hù)按下操作鍵或點(diǎn)擊鼠標(biāo)按照時(shí)被喚醒。當(dāng)Explorer的線(xiàn)程處于暫停狀態(tài)時(shí),系統(tǒng)不分配CPU給它的線(xiàn)程,這樣次優(yōu)先級(jí)的線(xiàn)程可以得到調(diào)度。但一旦用戶(hù)有按鍵操作,系統(tǒng)就會(huì)喚醒Explorer線(xiàn)程,如果低優(yōu)先級(jí)線(xiàn)程正在運(yùn)行,系統(tǒng)會(huì)立即搶在這些線(xiàn)程之前讓Explorer的線(xiàn)程優(yōu)先運(yùn)行。應(yīng)該避免使用實(shí)時(shí)這個(gè)最高的優(yōu)先級(jí)類(lèi),因?yàn)樗赡芨缮娌僮飨到y(tǒng)任務(wù)的運(yùn)行,可能阻止必要的磁盤(pán)I/O信息和網(wǎng)絡(luò)信息的產(chǎn)生。
當(dāng)調(diào)用CreateProcess時(shí),fdwCreate參數(shù)可以傳遞需要的優(yōu)先級(jí)類(lèi)。可通過(guò)調(diào)用SetPriorityClass來(lái)改變優(yōu)先級(jí)類(lèi):
BOOL SetPriorityClass(HANDLE hProcess,DWORD fdwPriority);
該函數(shù)將hProcess標(biāo)識(shí)的優(yōu)先級(jí)改為fdwPriority參數(shù)中設(shè)定的值。由于該函數(shù)帶有一個(gè)進(jìn)程句柄,所以只要擁有進(jìn)程的句柄和足夠的訪(fǎng)問(wèn)權(quán),就可以改變系統(tǒng)中運(yùn)行的任何進(jìn)程的優(yōu)先級(jí)類(lèi)。檢索進(jìn)程的優(yōu)先級(jí)類(lèi)函數(shù):DWORD GetPriorityClass(HANDLE hProcess)。如果使用Start命令來(lái)啟動(dòng)程序,可以使用一個(gè)開(kāi)關(guān)來(lái)設(shè)定應(yīng)用程序的起始優(yōu)先級(jí),如在命令外殼輸入如下命令可使系統(tǒng)啟動(dòng)Calculator,并在開(kāi)始時(shí)按空閑優(yōu)先級(jí)來(lái)運(yùn)行它:
C:/>START /LOW CALC.EXE
Start命令還能識(shí)別/BELOWNORMAL、/NORMAL、/ABOVENORMAL、/HIGT和/REALTIME等開(kāi)關(guān),以便按它們各自的優(yōu)先級(jí)啟動(dòng)執(zhí)行一個(gè)應(yīng)用程序。當(dāng)然,一旦應(yīng)用程序啟動(dòng)運(yùn)行,可以通過(guò)調(diào)用SetPriorityClass函數(shù)改變自己的優(yōu)先級(jí)。
當(dāng)系統(tǒng)將線(xiàn)程分配給處理器時(shí),Windows2000使用軟親緣性來(lái)進(jìn)程操作,這意味著如果所有其他因素相同的話(huà),它將設(shè)法在它上次運(yùn)行的哪個(gè)處理器上運(yùn)行線(xiàn)程,讓線(xiàn)程留在單個(gè)處理器上,有助于重復(fù)使用仍然在處理器的內(nèi)存高速緩存中的數(shù)據(jù)。Windows2000允許設(shè)置進(jìn)程和線(xiàn)程的親緣性,可控制哪個(gè)CPU運(yùn)行某些線(xiàn)程,稱(chēng)為硬親緣性。計(jì)算機(jī)在引導(dǎo)時(shí),要確定機(jī)器中有多少個(gè)CPU可供使用。通過(guò)調(diào)用GetSystemInfo函數(shù),應(yīng)用程序可查詢(xún)機(jī)器中的CPU數(shù)量。按照默認(rèn)設(shè)置,任何線(xiàn)程都可以調(diào)度到這些CPU中的任何一個(gè)上去運(yùn)行。為限制在可用CPU的子集上運(yùn)行的單個(gè)進(jìn)程中的線(xiàn)程數(shù)量,可調(diào)用:
BOOL SetProcessAffinityMask(HANDLE hProcess,DWORD_PTR dwProcessAffinityMask);
第一個(gè)參數(shù)hProcess用于指明影響的是哪個(gè)進(jìn)程;第二個(gè)參數(shù)dwProcessAffinityMask是位屏蔽,用于指明線(xiàn)程可以在那些CPU上運(yùn)行,如傳遞0x00000005(二進(jìn)制0101,0和2位是真值)表示該進(jìn)程中的線(xiàn)程可以在CPU0和CPU2上運(yùn)行,但是不能愛(ài)CPU1和CPU3至31上運(yùn)行。子進(jìn)程可以繼承進(jìn)程的親緣性。同時(shí)可通過(guò)下面函數(shù)返回進(jìn)程的親緣性屏蔽:
BOOL GetProcessAffinityMask(HANDLE hProcess,
???????????????????????????????????????????????? PDWORD_PTR pdwProcessAffinityMask,
???????????????????????????????????????????????? PDWORD_PTR pdwSystemAffinityMask);
傳遞親緣性屏蔽的進(jìn)程句柄,函數(shù)將填入pdwProcessAffinityMask變量,同時(shí)返回系統(tǒng)的親緣性屏蔽(pdwSystemAffinityMask指向的變量中)。系統(tǒng)的親緣性屏蔽用于指明系統(tǒng)的那個(gè)CPU能夠處理線(xiàn)程,進(jìn)程的親緣性始終是一個(gè)系統(tǒng)的親緣性屏蔽的正確子集。上面談到的是將進(jìn)程的多個(gè)線(xiàn)程限制到一組CPU上運(yùn)行,那么同樣可以設(shè)置將進(jìn)程中的一個(gè)線(xiàn)程限制到一組CPU上去運(yùn)行。如包含4個(gè)線(xiàn)程的進(jìn)程,在擁有4個(gè)CPU的計(jì)算機(jī)上運(yùn)行,如為線(xiàn)程中的一個(gè)線(xiàn)程(正在執(zhí)行非常重要的操作)增加某個(gè)CPU始終供它使用,則需要對(duì)其他三個(gè)線(xiàn)程限制不能在CPU0上運(yùn)行,而只能在CPU1、CPU2、CPU3上運(yùn)行。
通過(guò)調(diào)用SetThreadAffinityMask函數(shù),能為各個(gè)線(xiàn)程設(shè)置親緣性屏蔽:
DWORD_PTR SetThreadAffinityMask(HANDLE hThread,
DWORD_PTR dwThreadAffinityMask);
函數(shù)中hThread參數(shù)用于指明要限制的線(xiàn)程,dwThreadAffinityMask用于指明線(xiàn)程能夠運(yùn)行在那個(gè)CPU上,dwThreadAffinityMask是進(jìn)程親緣性的相應(yīng)子集,返回值是線(xiàn)程的前一個(gè)親緣性屏蔽。上面例子中將3個(gè)線(xiàn)程限制在CPU1、CPU2、CPU3上運(yùn)行的代碼:
//Thread 0 can only on CPU0.
SetThreadAffinityMask(hThread0,0x00000001);
//Threads1/2/3 run on CPUs 1/2/3
SetThreadAffinityMask(hThread1,0x0000000E);
SetThreadAffinityMask(hThread2,0x0000000E);
SetThreadAffinityMask(hThread3,0x0000000E);
?????????????????? 如非 2008-12-22
總結(jié)
以上是生活随笔為你收集整理的线程的调度、优先级和亲缘性——Windows核心编程学习手札系列之七的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 泛架构之于外包IT工程
- 下一篇: 线程与内核对象的同步——Windows核