线程与内核对象的同步——Windows核心编程学习手札之九
線(xiàn)程與內(nèi)核對(duì)象的同步
——Windows核心編程學(xué)習(xí)手札之九
用戶(hù)方式下的線(xiàn)程同步機(jī)制具有速度快的特點(diǎn),但有其局限性,對(duì)于許多應(yīng)用程序來(lái)說(shuō),并不合適。例如,互鎖函數(shù)家族只能在單值上運(yùn)行,根本無(wú)法使線(xiàn)程進(jìn)入等待狀態(tài);關(guān)鍵代碼可以使線(xiàn)程進(jìn)入等待狀態(tài),但只能用關(guān)鍵代碼段對(duì)單個(gè)進(jìn)程中的線(xiàn)程實(shí)施同步,使用關(guān)鍵代碼段也容易陷入死鎖,因?yàn)樵诘却M(jìn)入關(guān)鍵代碼段時(shí)無(wú)法設(shè)定超時(shí)值。內(nèi)核方式下的線(xiàn)程同步機(jī)制適應(yīng)性要優(yōu)于用戶(hù)方式下的機(jī)制,唯一不足的是內(nèi)核對(duì)象機(jī)制的速度比較慢。內(nèi)核對(duì)象機(jī)制下線(xiàn)程必須從用戶(hù)方式轉(zhuǎn)為內(nèi)核方式,這個(gè)轉(zhuǎn)換需要很大代價(jià):往返一次需要占用X86平臺(tái)上大約1000個(gè)CPU周期,當(dāng)然還不包括執(zhí)行內(nèi)核方式代碼,即實(shí)現(xiàn)線(xiàn)程調(diào)用的函數(shù)的代碼所需時(shí)間。
對(duì)于線(xiàn)程同步來(lái)說(shuō),內(nèi)核對(duì)象中的每種對(duì)象(包括進(jìn)程、線(xiàn)程、作業(yè))都可以說(shuō)是處于已通知或未通知的狀態(tài)之中,這種狀態(tài)的切換是由Microsoft為每個(gè)對(duì)象建立的一套規(guī)則來(lái)決定。例如,進(jìn)程內(nèi)核對(duì)象總是未通知的狀態(tài)中創(chuàng)建的,當(dāng)進(jìn)程終止運(yùn)行時(shí),操作系統(tǒng)自動(dòng)使該進(jìn)程的內(nèi)核對(duì)象處于已通知狀態(tài),一旦進(jìn)程內(nèi)核對(duì)象得到通知,它將永遠(yuǎn)保持這種狀態(tài),它的狀態(tài)永遠(yuǎn)不會(huì)改為未通知狀態(tài)。當(dāng)進(jìn)程正在運(yùn)行時(shí),進(jìn)程內(nèi)核對(duì)象處于未通知狀態(tài),當(dāng)進(jìn)程終止運(yùn)行時(shí),就變?yōu)橐淹ㄖ獱顟B(tài)。線(xiàn)程的內(nèi)核對(duì)象如是,因此可以將相同方法應(yīng)用于程序。下面的內(nèi)核對(duì)象可以處于已通知狀態(tài)或未通知狀態(tài):1)進(jìn)程;2)文件修改通知;3)線(xiàn)程;4)事件;5)作業(yè);6)可等待定時(shí)器;7)文件;8)信標(biāo);9)控制臺(tái)輸入;10)互斥對(duì)象。線(xiàn)程可以使自己進(jìn)入等待狀態(tài),直到一個(gè)對(duì)象變?yōu)橐淹ㄖ獱顟B(tài)。注意,用于控制每個(gè)對(duì)象的已通知/未通知狀態(tài)的規(guī)則要根據(jù)對(duì)象類(lèi)型而定。Windows提供專(zhuān)門(mén)實(shí)現(xiàn)線(xiàn)程同步的各種內(nèi)核對(duì)象,如事件、等待計(jì)數(shù)器、信標(biāo)和互斥對(duì)象。
等待函數(shù)可使線(xiàn)程資源進(jìn)入等待狀態(tài),直到一個(gè)特定的內(nèi)核對(duì)象變?yōu)橐淹ㄖ獱顟B(tài)為止,這些等待函數(shù)中最常用的是WaitForSingleObject:
DWORD WaitForSingleObject(
?????????????????????????????????? HANDLE hObject,
?????????????????????????????????? DWORD dwMillisecondes);
當(dāng)線(xiàn)程調(diào)用該函數(shù)時(shí),第一個(gè)參數(shù)hObject標(biāo)識(shí)一個(gè)能夠支持被通知/未通知的內(nèi)核對(duì)象,第二個(gè)參數(shù)dwMilliseconds運(yùn)行線(xiàn)程設(shè)置等待的時(shí)間長(zhǎng)度。
函數(shù)WaitForMultipleObjects允許調(diào)用線(xiàn)程同時(shí)查看若干個(gè)內(nèi)核對(duì)象的已通知狀態(tài):
DWORD WaitForMultipleObjects(
?????????????????????????????????? DWORD dwCount,
?????????????????????????????????? CONST HANDLE *phObjects,
?????????????????????????????????? BOOL fWailAll,
?????????????????????????????????? DWORD dwMilliseconds);
該函數(shù)的參數(shù)dwCount用于指明想要讓函數(shù)查看的內(nèi)核對(duì)象的數(shù)量,這個(gè)值必須在1和MAXIMUN_WAIT_OBJECTS(在Windows頭文件中定義為64)之間;參數(shù)phObjects參數(shù)是指向內(nèi)核對(duì)象句柄的數(shù)組的指針。參數(shù)fWaitAll告訴函數(shù)是在指定內(nèi)核對(duì)象中任何一個(gè)變?yōu)橐淹ㄖ獱顟B(tài)還是在所有指定的內(nèi)核對(duì)象都變?yōu)橐淹ㄖ獱顟B(tài)下線(xiàn)程可運(yùn)行,如fWaitAll傳遞了TRUE,那么在所有對(duì)象變?yōu)橐淹ㄖ?#xff0c;該函數(shù)將不運(yùn)行調(diào)用線(xiàn)程運(yùn)行;dwMilliseconds與WaitForSingleObject作用一樣,用于設(shè)置等待時(shí)長(zhǎng)。
事件內(nèi)核對(duì)象是最基本的對(duì)象,包含一個(gè)使用計(jì)數(shù)(與所有內(nèi)核對(duì)象一樣)、一個(gè)用于指明該事件是個(gè)自動(dòng)重置的事件還是一個(gè)人工重置的事件的布爾值、用于指明該事件處于已通知狀態(tài)還是未通知狀態(tài)的布爾值。事件能夠通知一個(gè)操作已經(jīng)完成,有兩種不同類(lèi)型的事件對(duì)象,一種是人工重置的事件,另一種是自動(dòng)重置的事件。當(dāng)人工重置的事件得到通知時(shí),等待該事件的所有線(xiàn)程均變?yōu)榭烧{(diào)度的線(xiàn)程;當(dāng)一個(gè)自動(dòng)重置的事件得到通知時(shí),等待該事件的線(xiàn)程中只有一個(gè)線(xiàn)程變?yōu)榭烧{(diào)度的線(xiàn)程。當(dāng)一個(gè)線(xiàn)程執(zhí)行初始化操作,然后通知另一個(gè)線(xiàn)程執(zhí)行剩余的操作時(shí),事件使用得最多。事件初始化為未通知狀態(tài),在該線(xiàn)程完成它的初始化操作后,它將事件設(shè)置為已通知狀態(tài),這時(shí),等待該事件的另一個(gè)線(xiàn)程發(fā)現(xiàn)該事件已經(jīng)得到通知,此線(xiàn)程序就變成可調(diào)度線(xiàn)程。
創(chuàng)建事件內(nèi)核對(duì)象:
HANDLE CreateEvent(
?????????????????????????????????? PSECURITY_ATTRIBUTES psa,
?????????????????????????????????? BOOL fManualReset,
?????????????????????????????????? BOOL fInitialState,
?????????????????????????????????? PCTSTR pszName);
其中,fManualReset參數(shù)是布爾值,告訴系統(tǒng)創(chuàng)建人工重置的事件(TRUE)或是自動(dòng)重置事件(FALSE);fInitialState參數(shù)用于指明該事件是要初始化為已通知狀態(tài)(TRUE)還是未通知狀態(tài)(FALSE);當(dāng)系統(tǒng)創(chuàng)建事件對(duì)象后,createEvent就將與進(jìn)程相關(guān)的句柄返回給事件對(duì)象,其他進(jìn)程中的線(xiàn)程可以獲得對(duì)該對(duì)象的訪(fǎng)問(wèn)權(quán),方法是使用在pszName參數(shù)中傳遞的相同值,使用繼承性,使用DuplicateHandle函數(shù)等來(lái)調(diào)用CreateEvent,或者調(diào)用OpenEvent,在pszName參數(shù)中設(shè)定一個(gè)與調(diào)用createEvent時(shí)設(shè)定的名字相匹配的名字:
HANDLE OpenEvent(
??????????????????????????? DWORD fdwAccess,
??????????????????????????? BOOL fInherit,
??????????????????????????? PCTSTR pszName);
與其他內(nèi)核對(duì)象一樣,當(dāng)不再需要事件內(nèi)核對(duì)象時(shí),應(yīng)調(diào)用CloseHandle函數(shù)。
一旦事件已經(jīng)創(chuàng)建,就可以直接控制它的狀態(tài),當(dāng)調(diào)用SetEvent時(shí),可以將事件改為已通知狀態(tài):BOOL SetEvent(HANDLE hEvent);當(dāng)調(diào)用ResetEvent(HANDLE hEvent)函數(shù)時(shí),就可以將該事件改為未通知狀態(tài)。函數(shù)BOOL PulseEvent(HANDLE hEvent)使得事件變?yōu)橐淹ㄖ獱顟B(tài),然后立即變?yōu)槲赐ㄖ獱顟B(tài),如調(diào)用SetEvent后又立即調(diào)用ResetEvent函數(shù)。
下面的代碼例子說(shuō)明了自動(dòng)重置事件的用法,具體是實(shí)現(xiàn)了由主線(xiàn)程向子線(xiàn)程發(fā)起請(qǐng)求時(shí)設(shè)置事件未通知狀態(tài),子線(xiàn)程響應(yīng)主線(xiàn)程序請(qǐng)求:
#include <process.h>
//this event is signaled when the client has a request for server
HANDLE g_hEventRequestSubmitted;
//this event is signaled when the server has a result for the client
HANDLE g_hEventResultReturned;
//the buffer shared between the client an server threads
TCHAR g_szSharedRequestAndResultBuffer[1024];
//server thread to terminate cleanly
TCHAR g_szServerShutdown[]=TEXT("Server Shutdown");
?
//this is the code executed by the server thread
UINT WINAPI ServerThread(PVOID pvParam)
{
?????? //assume that the server thread is to run forever
?????? BOOL fShutdown=FALSE;
?????? while(!fShutdown)
?????? {
????????????? //wait for the client to submit a request
????????????? WaitForSingleObject(g_hEventRequestSubmitted,INFINITE);
????????????? //check to see if the client wants the server to terminate
????????????? fShutdown=(lstrcmpi(g_szSharedRequestAndResultBuffer,g_szServerShutdown)==0);
????????????? if(!fShutdown)
????????????? {
???????????????????? //process the client's requet(reverse the string)
???????????????????? _tcsrev(g_szSharedRequestAndResultBuffer);
????????????? }
?
????????????? //let the client process the request's result
????????????? SetEvent(g_hEventResultReturned);
?????? }
?????? return 0;
}
?
void CEventDemoDlg::OnOK()
{
?????? // TODO: Add extra validation here
?????? //create & initialize the two nonsignaled ,auto-reset events
?????? g_hEventRequestSubmitted=CreateEvent(NULL,FALSE,FALSE,NULL);
?????? g_hEventResultReturned=CreateEvent(NULL,FALSE,FALSE,NULL);
?
?????? //spawn the server thread
?????? UINT dwThreadID;
?????? HANDLE hThreadServer=(HANDLE)_beginthreadex(NULL,0,ServerThread,NULL,0,&dwThreadID);
?
?????? CString strReq;
?????? GetDlgItemText(IDC_EditReq,strReq);
?????? strcpy(g_szSharedRequestAndResultBuffer,strReq);
?
?????? //let the server thread know that a request is ready in the buffer
?????? SetEvent(g_hEventRequestSubmitted);
?
?????? //wait for the server to process the request and give us the result
?????? WaitForSingleObject(g_hEventResultReturned,INFINITE);
?????? //let the user know the result
?????? strReq.Format(_T("%s"), g_szSharedRequestAndResultBuffer);
?????? SetDlgItemText(IDC_EditRes,strReq);
?
?
?????? //end
?????? lstrcpy(g_szSharedRequestAndResultBuffer,g_szServerShutdown);
?????? SetEvent(g_hEventRequestSubmitted);
?????? //wait for the server thread to acknowledge the shutdown and wait for the server thread to fully terminate
?????? HANDLE h[2];
?????? h[0]=g_hEventResultReturned;
?????? h[1]=hThreadServer;
?????? WaitForMultipleObjects(2,h,TRUE,INFINITE);
?????? //properly clean up everything
?????? CloseHandle(hThreadServer);
?????? CloseHandle(g_hEventRequestSubmitted);
?????? CloseHandle(g_hEventResultReturned);
?????? //close the application
//???? CDialog::OnOK();
?????? AfxMessageBox("close the application");
}
void CEventDemoDlg::OnOK() 是VC6.0中Dialog工程的一個(gè)按鈕函數(shù),在這里具體實(shí)現(xiàn)了事件的狀態(tài)變化來(lái)同步線(xiàn)程的運(yùn)行。
等待定時(shí)器內(nèi)核對(duì)象是在某個(gè)時(shí)間或按規(guī)定的間隔時(shí)間發(fā)出自己的信號(hào)來(lái)通知,用在某個(gè)時(shí)間執(zhí)行某個(gè)操作,其函數(shù)是:
HANDLE CreateWaitableTimer(
??????????????????????????? PSECURITY_ATTRIBUTES psa,
??????????????????????????? BOOL fManualReset,
??????????????????????????? PCTSTR pszName);
進(jìn)程可以獲得與自己相關(guān)的現(xiàn)有等待定時(shí)器的句柄,通過(guò)函數(shù):
HANDLE OpenWaitableTimer(
??????????????????????????? DWORD dwDesiredAccess,
??????????????????????????? BOOL bInheritHandle,
??????????????????????????? PCTSTR pszName);
與事件內(nèi)核對(duì)象一樣,fManualReset參數(shù)用于指明人工重置的定時(shí)器或自動(dòng)重置的定時(shí)器。當(dāng)發(fā)出人工重置的定時(shí)器信號(hào)通知時(shí),等待該定時(shí)器的所有線(xiàn)程均變?yōu)榭烧{(diào)度的線(xiàn)程,當(dāng)發(fā)出自動(dòng)重置的定時(shí)器信號(hào)通知時(shí),只有一個(gè)等待的線(xiàn)程變?yōu)榭烧{(diào)度線(xiàn)程。
等待定時(shí)器總是在未通知狀態(tài)中創(chuàng)建,必須調(diào)用SetWaitableTimer函數(shù)來(lái)告訴定時(shí)器使其變?yōu)橐淹ㄖ獱顟B(tài):
BOOL SetWaitableTimer(
??????????????????????????? HANDLE hTimer,
??????????????????????????? Const LARGE_INTEGER *pDueTime,
??????????????????????????? LONG lPeriod,
??????????????????????????? PTIMERAPCROUTINE pfnCompletionRoutine,
??????????????????????????? PVOID pvArgToCompletionRoutine,
??????????????????????????? BOOL fResume);
其中,參數(shù)hTimer用于指明所設(shè)置的定時(shí)器;PDueTime和lPeriod兩個(gè)參數(shù)是一同使用的,PDueTime指明定時(shí)器何時(shí)應(yīng)第一次報(bào)時(shí),而lPeriod參數(shù)則指明此后定時(shí)器應(yīng)間隔多長(zhǎng)時(shí)間報(bào)時(shí)一次,下面代碼用于將定時(shí)器的第一次報(bào)時(shí)設(shè)置在2009年1月8日的下午一點(diǎn)鐘,然后每隔6小時(shí)報(bào)時(shí)一次:
//Declare our local variables.
HANDLE hTimer;
SYSTEMTIME st;
FILETIME ftLocal,ftUTC;
LARGE_INTEGER liUTC;
//create an auto-reset timer.
hTimer=CreateWaitableTimer(NULL,FALSE,NULL);
//first signaling is at January 8, 2009 at 1:00P.M. (local time).
st.wYear=2009;
st.wMonth=1;
st.wDayofWeek=0;
st.wDay=8;
st.wHour=13;
st.wMinute=0;
st.wSeconde=0;
st.wMilliseconds=0;
SystemTimeToFileTime(&st,&ftLocal);
//convert local time to UTC time.
LocalFileTimeToFileTime(&ftLocal,&ftUTC);
//convert FILETIME to LARGE_INTEGER because of different alignment.
liUTC.LowPart=ftUTC.dwLowDateTime;
liUIC.HighPart=ftUTC.dwHighDateTime;
//set the timer.
SetWaitableTimer(hTimer,&liUTC,6*60*60*1000,NULL,NULL,FALSE);
……
首先對(duì)SYSTEMTIME結(jié)構(gòu)進(jìn)行初始化,該結(jié)果用于指明定時(shí)器何時(shí)第一次報(bào)時(shí)(發(fā)出信號(hào)通知)。
信標(biāo)內(nèi)核對(duì)象用于對(duì)資源進(jìn)行計(jì)數(shù),與所有內(nèi)核對(duì)象一樣,包含一個(gè)使用數(shù)量,但也包含另外兩個(gè)帶符號(hào)的32位值,一個(gè)是最大資源數(shù)量,一個(gè)是當(dāng)前資源數(shù)量,最大資源數(shù)量用于標(biāo)識(shí)信標(biāo)能夠控制的資源的最大數(shù)量,而當(dāng)前資源數(shù)量則用于標(biāo)識(shí)當(dāng)前可以使用的資源的數(shù)量。信標(biāo)的使用規(guī)則是:1)如果當(dāng)前資源的數(shù)量大于0,則發(fā)出信標(biāo)信號(hào);2)如果當(dāng)前資源數(shù)量是0,則不發(fā)出信標(biāo)信號(hào);3)系統(tǒng)決不允許當(dāng)前資源的數(shù)量為負(fù)值;4)當(dāng)前資源數(shù)量決不能大于最大資源數(shù)量。當(dāng)使用信標(biāo)時(shí),不要將信標(biāo)對(duì)象的使用數(shù)量和它的當(dāng)前資源數(shù)量混為一談。創(chuàng)建信標(biāo)內(nèi)核對(duì)象的函數(shù)是:
HANDLE CreateSemaphore(
?????????????????????????????????? PSECURITY_ATTRIBUTE psa,
?????????????????????????????????? LONG lInitialCount,
?????????????????????????????????? LONG lMaximumCount,
?????????????????????????????????? PCTSTR pszName);
通過(guò)函數(shù)OpenSemaphore函數(shù),其他進(jìn)程可以獲得現(xiàn)有信標(biāo)有關(guān)的句柄:
HANDLE OpenSemaphore(
?????????????????????????????????? DWORD fdwAccess,
?????????????????????????????????? BOOL bInheritHandle,
?????????????????????????????????? PCTSTR pszName);
lMaximumCount參數(shù)用于告訴系統(tǒng),應(yīng)用程序處理的最大資源數(shù)量是多少,該參數(shù)是帶符號(hào)的32位值,因此最多可以擁有2147483647個(gè)資源。lInitialCount參數(shù)用于指明開(kāi)始時(shí)(當(dāng)前)這些資源有多少可供使用。信標(biāo)能以原子操作方式執(zhí)行測(cè)試和設(shè)置操作,也就是說(shuō),當(dāng)向信標(biāo)申請(qǐng)一個(gè)資源時(shí),操作系統(tǒng)就要檢查是否有這個(gè)資源可供使用,同時(shí)將可用資源的數(shù)量遞減,而不讓另一個(gè)線(xiàn)程加以干擾,只有當(dāng)資源數(shù)量遞減后,系統(tǒng)才允許另一個(gè)線(xiàn)程申請(qǐng)對(duì)資源的訪(fǎng)問(wèn)權(quán)。通過(guò)調(diào)用ReleaseSemaphore函數(shù),線(xiàn)程能夠?qū)π艠?biāo)的當(dāng)前資源數(shù)量進(jìn)行遞增:
BOOL ReleaseSemaphore(
?????????????????????????????????? HANDLE hsem,
?????????????????????????????????? LONG lReleaseCount,
?????????????????????????????????? PLONG plPreviousCount);
該函數(shù)將lReleaseCount中的值添加給信標(biāo)的當(dāng)前資源數(shù)量。
互斥對(duì)象(mutex)內(nèi)核對(duì)象能夠確保線(xiàn)程擁有對(duì)單個(gè)資源的互斥訪(fǎng)問(wèn)權(quán),包含一個(gè)使用數(shù)量,一個(gè)線(xiàn)程ID和一個(gè)遞歸計(jì)數(shù)器,互斥對(duì)象的行為特性和關(guān)鍵代碼段相同,但是互斥對(duì)象屬于內(nèi)核對(duì)象,而關(guān)鍵代碼段則屬于用戶(hù)方式對(duì)象,這意味著互斥對(duì)象的運(yùn)行速度比關(guān)鍵代碼段要慢,但也意味著不同進(jìn)程的多個(gè)線(xiàn)程能夠訪(fǎng)問(wèn)單個(gè)互斥對(duì)象,并且線(xiàn)程在等待訪(fǎng)問(wèn)資源時(shí)可以設(shè)定一個(gè)超時(shí)值。ID用于標(biāo)識(shí)系統(tǒng)中的那個(gè)線(xiàn)程當(dāng)前擁有互斥對(duì)象,遞歸計(jì)數(shù)器用于指明該線(xiàn)程擁有互斥對(duì)象的次數(shù)。互斥對(duì)象是常用內(nèi)核對(duì)象之一,通常是用于保護(hù)由多個(gè)線(xiàn)程訪(fǎng)問(wèn)的內(nèi)存塊,互斥對(duì)象保證訪(fǎng)問(wèn)內(nèi)存塊的任何線(xiàn)程擁有對(duì)該內(nèi)存塊的獨(dú)占訪(fǎng)問(wèn)權(quán),這樣可保證數(shù)據(jù)的完整性。互斥對(duì)象使用規(guī)則如下:
1)? 如果線(xiàn)程ID是0(這是無(wú)效的ID值),互斥對(duì)象不被任何線(xiàn)程所擁有,并且發(fā)出該互斥對(duì)象的通知信號(hào);
2)? 如果ID是非0數(shù)字,那么一個(gè)線(xiàn)程就擁有互斥對(duì)象,并且不發(fā)出該互斥對(duì)象的通知信號(hào);
3)? 與所有其他內(nèi)核對(duì)象不同,互斥對(duì)象在操作系統(tǒng)中擁有特殊的代碼,允許它們違反正常的規(guī)則;
若要使用互斥對(duì)象,需要調(diào)用CreateMutex來(lái)創(chuàng)建互斥內(nèi)核對(duì)象:
HANDLE CreateMutex(
??????????????????????????? PSECURITY_ATTRIBUTES psa,
??????????????????????????? BOOL fInitialOwner,
??????????????????????????? PCTSTR pszName);
通過(guò)調(diào)用OpenMutex,另一個(gè)進(jìn)程可以獲得現(xiàn)有互斥對(duì)象相關(guān)的句柄:
HANDLE OpenMutex(
??????????????????????????? DWORD fdwAccess,
??????????????????????????? BOOL bInheritHandle,
??????????????????????????? PCTSTR pszName);
其中,參數(shù)fInitialOwner用于控制互斥對(duì)象的初始狀態(tài),如果傳遞FALSE(通常情況下傳遞的值),那互斥對(duì)象的ID和遞歸計(jì)數(shù)器被設(shè)置為0,這意味著該互斥對(duì)象沒(méi)有被任何線(xiàn)程所擁有,因此要發(fā)出它的通知信號(hào)。如果fInitialOwner參數(shù)傳遞TRUE,那么該對(duì)象的線(xiàn)程ID被設(shè)置為調(diào)用線(xiàn)程的ID,遞歸計(jì)數(shù)器被設(shè)置為1,由于ID是個(gè)非0數(shù)字,因此該互斥對(duì)象開(kāi)始時(shí)不發(fā)出通知信號(hào)。
對(duì)于互斥對(duì)象來(lái)說(shuō),正常的內(nèi)核對(duì)象的已通知和未通知規(guī)則存在一個(gè)特殊的異常情況,比如說(shuō):一個(gè)線(xiàn)程試圖等待一個(gè)未通知的互斥對(duì)象,在這種情況下,該線(xiàn)程通常被置于等待狀態(tài),然而系統(tǒng)要查看試圖獲取互斥對(duì)象的線(xiàn)程ID是否與互斥對(duì)象中記錄的線(xiàn)程ID相同,如果相同,即使互斥對(duì)象處于未通知狀態(tài),系統(tǒng)也允許線(xiàn)程保持可調(diào)度狀態(tài),這種“異常”行為特性不適用于系統(tǒng)中的其他內(nèi)核對(duì)象。每當(dāng)線(xiàn)程成功地等待互斥對(duì)象時(shí),該對(duì)象的遞歸計(jì)數(shù)器就遞增,若要使遞歸計(jì)數(shù)器的值大于1,唯一的方法是線(xiàn)程多次等待相同的互斥對(duì)象,以便利用這個(gè)異常規(guī)則。一旦線(xiàn)程成功等待到一個(gè)互斥對(duì)象,該線(xiàn)程就知道它已經(jīng)擁有對(duì)受保護(hù)資源的獨(dú)占訪(fǎng)問(wèn)權(quán),試圖訪(fǎng)問(wèn)該資源的任何其他線(xiàn)程(通過(guò)等待相同的互斥對(duì)象)均被置于等待狀態(tài)中。當(dāng)目前擁有對(duì)資源訪(fǎng)問(wèn)權(quán)的線(xiàn)程不再需要它的訪(fǎng)問(wèn)權(quán)時(shí),需要調(diào)用ReleaseMutex函數(shù)來(lái)釋放該互斥對(duì)象:
BOOL ReleaseMutex(HANDLE hMutex);
該函數(shù)將對(duì)象遞歸技數(shù)器遞減1,如果線(xiàn)程多次成功等待一個(gè)互斥對(duì)象,在互斥對(duì)象的遞歸計(jì)數(shù)器變成0之前,該線(xiàn)程必須以同樣次數(shù)調(diào)用ReleaseMutex函數(shù),當(dāng)遞歸計(jì)數(shù)器到達(dá)0時(shí),該線(xiàn)程ID也被置為0,同時(shí)該對(duì)象變?yōu)橐淹ㄖ獱顟B(tài)。當(dāng)一個(gè)線(xiàn)程調(diào)用ReleaseMutex函數(shù)時(shí),函數(shù)要查看調(diào)用線(xiàn)程的ID是否與互斥對(duì)象中的線(xiàn)程ID相匹配,如相同,遞歸計(jì)數(shù)器就遞減,如不匹配,那函數(shù)將不進(jìn)行任何操作,而是將FALSE(表示失敗)返回給調(diào)用者,此時(shí)調(diào)用GetLastError,將返回ERROR_NOT_OWNER(試圖釋放不是調(diào)用者擁有的互斥對(duì)象)。如在釋放互斥對(duì)象之前,擁有互斥對(duì)象的線(xiàn)程終止運(yùn)行(使用ExitThread、TerminateThread、ExitProcess或TerminateProcess函數(shù)),系統(tǒng)將該互斥對(duì)象視為已經(jīng)被放棄——擁有互斥的線(xiàn)程決不會(huì)釋放它,因?yàn)樵摼€(xiàn)程已經(jīng)終止運(yùn)行。
總結(jié)下線(xiàn)程同步對(duì)象(內(nèi)核對(duì)象)與線(xiàn)程同步之間的相互關(guān)系:
1)對(duì)象:進(jìn)程,何時(shí)處于未通知狀態(tài):當(dāng)進(jìn)程仍然活動(dòng)時(shí),何時(shí)處于已通知狀態(tài):當(dāng)進(jìn)程終止運(yùn)行時(shí)(ExitProcess,TerminateProcess),成功等待的副作用:無(wú);
2)對(duì)象:線(xiàn)程,何時(shí)處于未通知狀態(tài):當(dāng)線(xiàn)程仍然活動(dòng)時(shí),何時(shí)處于已通知狀態(tài):當(dāng)線(xiàn)程終止運(yùn)行時(shí)(ExitThread,TerminateThread),成功等待的副作用:無(wú);
3)對(duì)象:作業(yè),何時(shí)處于未通知狀態(tài):當(dāng)作業(yè)的時(shí)間尚未結(jié)束時(shí),何時(shí)處于已通知狀態(tài):當(dāng)作業(yè)的時(shí)間結(jié)束時(shí),成功等待的副作用:無(wú);
4)對(duì)象:文件,何時(shí)處于未通知狀態(tài):當(dāng)I/O請(qǐng)求正在處理時(shí),何時(shí)處于已通知狀態(tài):當(dāng)I/O請(qǐng)求處理完畢時(shí),成功等待的副作用:無(wú);
5)對(duì)象:控制臺(tái)輸入,何時(shí)處于未通知狀態(tài):不存在任何輸入,何時(shí)處于已通知狀態(tài):當(dāng)存在輸入時(shí),成功等待的副作用:無(wú);
6)對(duì)象:文件修改通知,何時(shí)處于未通知狀態(tài):沒(méi)有任何文件被修改,何時(shí)處于已通知狀態(tài):當(dāng)文件系統(tǒng)發(fā)現(xiàn)修改時(shí),成功等待的副作用:重置通知;
7)對(duì)象:自動(dòng)重置事件,何時(shí)處于未通知狀態(tài):ResetEvent,Pulse-Event或等待成功,何時(shí)處于已通知狀態(tài):當(dāng)調(diào)用SetEvent/PulseEvent時(shí),成功等待的副作用:重置事件;
8)對(duì)象:人工重置事件,何時(shí)處于未通知狀態(tài):ResetEvent,Pulse-Event,何時(shí)處于已通知狀態(tài):當(dāng)調(diào)用SetEvent/PulseEvent時(shí),成功等待的副作用:無(wú)
9)對(duì)象:自動(dòng)重置等待定時(shí)器,何時(shí)處于未通知狀態(tài):CancelWaitableTimer或等待成功,何時(shí)處于已通知狀態(tài):當(dāng)時(shí)間到時(shí)(SetWaitableTimer),成功等待的副作用:重置定時(shí)器;
10)對(duì)象:人工重置等待定時(shí)器,何時(shí)處于未通知狀態(tài):CancelWaitableTimer,何時(shí)處于已通知狀態(tài):當(dāng)時(shí)間到時(shí)(SetWaitableTimer),成功等待的副作用:無(wú);
11)對(duì)象:信標(biāo),何時(shí)處于未通知狀態(tài):等待成功,何時(shí)處于已通知狀態(tài):當(dāng)數(shù)量>0時(shí)(ReleaseSemaphore),成功等待的副作用:數(shù)量遞減1;
12)對(duì)象:互斥對(duì)象,何時(shí)處于未通知狀態(tài):等待成功,何時(shí)處于已通知狀態(tài):當(dāng)未被線(xiàn)程擁有時(shí)(ReleaseMutex),成功等待的副作用:將所有權(quán)賦予線(xiàn)程;
13)對(duì)象:關(guān)鍵代碼段(用戶(hù)方式),何時(shí)處于未通知狀態(tài):等待成功((Try)EnterCriticalSection),何時(shí)處于已通知狀態(tài):當(dāng)未被線(xiàn)程擁有時(shí)(LeaveCriticalSection),成功等待的副作用:將所有權(quán)賦予線(xiàn)程。
互鎖(用戶(hù)方式)函數(shù)決不會(huì)導(dǎo)致線(xiàn)程變?yōu)榉钦{(diào)度狀態(tài),它們會(huì)改變一個(gè)值并立即返回。
線(xiàn)程同步函數(shù)WaitForSingleObject和WaitForMultipleObjects是使用最多的函數(shù),不過(guò),Windows還提供了其他函數(shù)。異步設(shè)備I/O使得線(xiàn)程能夠啟動(dòng)一個(gè)讀操作或?qū)懖僮?#xff0c;但是不必等待讀操作或?qū)懖僮魍瓿伞@?#xff0c;如果線(xiàn)程需要將一個(gè)大文件裝入內(nèi)存,那該線(xiàn)程可以告訴系統(tǒng)將文件裝入內(nèi)存,在系統(tǒng)加載該文件時(shí),線(xiàn)程可以執(zhí)行其他任務(wù),如創(chuàng)建窗口、對(duì)內(nèi)部數(shù)據(jù)結(jié)構(gòu)進(jìn)行初始化等等,當(dāng)初始化操作完成時(shí),線(xiàn)程可以終止自己的運(yùn)行,等待系統(tǒng)通知文件已經(jīng)讀取。設(shè)備對(duì)象是可以同步的內(nèi)核對(duì)象,這意味著可以調(diào)用WaitForSingleObject函數(shù),傳遞文件、套接字和通信端口的句柄。當(dāng)系統(tǒng)執(zhí)行異步I/O時(shí),設(shè)備對(duì)象處于未通知狀態(tài),一旦操作完成,系統(tǒng)就將對(duì)象的狀態(tài)改為已通知狀態(tài),這樣,線(xiàn)程就知道操作已經(jīng)完成,此時(shí)線(xiàn)程可繼續(xù)運(yùn)行。線(xiàn)程可以調(diào)用WaitForInputIdle來(lái)終止自己的運(yùn)行:
DWORD WaitForInputIdle(
??????????????????????????? HANDLE hProcess,
??????????????????????????? DWORD dwMilliseconds);
該函數(shù)一直處于等待狀態(tài),知道hProcess標(biāo)識(shí)的進(jìn)程在創(chuàng)建應(yīng)用程序的第一個(gè)窗口的線(xiàn)程中已經(jīng)沒(méi)有尚未處理的輸入為止。
線(xiàn)程可以調(diào)用MsgWaitForMultipleObjects或MsgWaitForMultipleObjectsEx函數(shù),讓線(xiàn)程等待它自己的消息:
DWORD MsgWaitForMultipleObjects(
????????????????????????????????????????? DWORD dwCount,
????????????????????????????????????????? PHANDLE phObjects,
????????????????????????????????????????? BOOL fWaitAll,
????????????????????????????????????????? DWORD dwMilliseconds,
????????????????????????????????????????? DWORD dwWakeMask);
DWORD MsgWaitForMultipleObjects(
????????????????????????????????????????? DWORD dwCount,
????????????????????????????????????????? PHANDLE phObjects,
????????????????????????????????????????? BOOL fWaitAll,
????????????????????????????????????????? DWORD dwMilliseconds,
????????????????????????????????????????? DWORD dwWakeMask,
DWORD dwFlags);
這些函數(shù)與WaitForMultipleObjects函數(shù)十分相似,差別在于它們?cè)试S線(xiàn)程在內(nèi)核對(duì)象變成已通知狀態(tài)或窗口消息需要調(diào)度到調(diào)用線(xiàn)程創(chuàng)建的窗口中時(shí)被調(diào)度。
線(xiàn)程同步函數(shù)還有WaitForDebugEvent函數(shù):當(dāng)調(diào)試程序啟動(dòng)運(yùn)行時(shí),它將自己附加給一個(gè)被調(diào)試程序;SingleObjectAndWait函數(shù)用于在單個(gè)原子方式的操作中發(fā)出關(guān)于內(nèi)核對(duì)象的通知并等待另一個(gè)內(nèi)核對(duì)象。
?????????????????????????????????? 如非
????????????????????????????????????? 2009-1-15
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專(zhuān)家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的线程与内核对象的同步——Windows核心编程学习手札之九的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 线程的调度、优先级和亲缘性——Windo
- 下一篇: 用户方式中线程的同步——Windows核