簡(jiǎn)介
線程之間
通信的兩個(gè)基本問題是互斥和同步。
線程同步是指線程之間所具有的一種制約關(guān)系,一個(gè)線程的執(zhí)行依賴另一個(gè)線程的消息,當(dāng)它沒有得到另一個(gè)線程的消息時(shí)應(yīng)等待,直到消息到達(dá)時(shí)才被喚醒。
線程互斥是指對(duì)于共享的
操作系統(tǒng)資源(指的是廣義的"資源",而不是
Windows的.res文件,譬如全局變量就是一種共享資源),在各線程訪問時(shí)的排它性。當(dāng)有若干個(gè)線程都要使用某一共享資源時(shí),任何時(shí)刻最多只允許一個(gè)線程去使用,其它要使用該資源的線程必須等待,直到占用資源者釋放該資源。
線程互斥是一種特殊的線程同步。
實(shí)際上,互斥和同步對(duì)應(yīng)著線程間通信發(fā)生的兩種情況:
(1)當(dāng)有多個(gè)線程訪問共享資源而不使資源被破壞時(shí);
?。?)當(dāng)一個(gè)線程需要將某個(gè)任務(wù)已經(jīng)完成的情況通知另外一個(gè)或多個(gè)線程時(shí)。
在WIN32中,同步機(jī)制主要有以下幾種:
?。?)事件(Event);
?。?)信號(hào)量(semaphore);
(3)互斥量(mutex);
?。?)臨界區(qū)(Critical section)。
全局變量 因?yàn)檫M(jìn)程中的所有線程均可以訪問所有的全局變量,因而全局變量成為Win32多線程通信的最簡(jiǎn)單方式。例如:
int var; //全局變量 UINT ThreadFunction(LPVOIDpParam) { var = 0; while (var < MaxValue) { //線程處理 ::InterlockedIncrement(long*) &var); } return 0; } 請(qǐng)看下列程序: int globalFlag = false; DWORD WINAPI ThreadFunc(LPVOID n) { Sleep(2000); globalFlag = true;
return 0; }
int main() { HANDLE hThrd; DWORD threadId;
hThrd = CreateThread(NULL, 0, ThreadFunc, NULL, 0, &threadId); if (hThrd) { printf("Thread launched\n"); CloseHandle(hThrd); }
while (!globalFlag) ; printf("exit\n"); } |
上述程序中使用全局變量和while循環(huán)查詢進(jìn)行線程間同步,實(shí)際上,這是一種應(yīng)該避免的方法,因?yàn)椋?
(1)當(dāng)主線程必須使自己與ThreadFunc函數(shù)的完成運(yùn)行實(shí)現(xiàn)同步時(shí),它并沒有使自己進(jìn)入睡眠狀態(tài)。由于主線程沒有進(jìn)入睡眠狀態(tài),因此操作系統(tǒng)繼續(xù)為它調(diào)度C P U時(shí)間,這就要占用其他線程的寶貴時(shí)間周期;
?。?)當(dāng)主線程的優(yōu)先級(jí)高于執(zhí)行ThreadFunc函數(shù)的線程時(shí),就會(huì)發(fā)生globalFlag永遠(yuǎn)不能被賦值為true的情況。因?yàn)樵谶@種情況下,系統(tǒng)決不會(huì)將任何時(shí)間片分配給ThreadFunc線程。
事件 事件(Event)是WIN32提供的最靈活的線程間同步方式,事件可以處于激發(fā)狀態(tài)(signaled or true)或未激發(fā)狀態(tài)(unsignal or false)。根據(jù)狀態(tài)變遷方式的不同,事件可分為兩類:
?。?)手動(dòng)設(shè)置:這種對(duì)象只可能用程序手動(dòng)設(shè)置,在需要該事件或者事件發(fā)生時(shí),采用SetEvent及ResetEvent來(lái)進(jìn)行設(shè)置。
(2)自動(dòng)恢復(fù):一旦事件發(fā)生并被處理后,自動(dòng)恢復(fù)到?jīng)]有事件狀態(tài),不需要再次設(shè)置。
創(chuàng)建事件的函數(shù)原型為:
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, // SECURITY_ATTRIBUTES結(jié)構(gòu)指針,可為NULL BOOL bManualReset, // 手動(dòng)/自動(dòng) // TRUE:在WaitForSingleObject后必須手動(dòng)調(diào)用ResetEvent清除信號(hào) // FALSE:在WaitForSingleObject后,系統(tǒng)自動(dòng)清除事件信號(hào) BOOL bInitialState, //初始狀態(tài) LPCTSTR lpName //事件的名稱 ); |
使用"事件"機(jī)制應(yīng)注意以下事項(xiàng):
?。?)如果跨進(jìn)程訪問事件,必須對(duì)事件命名,在對(duì)事件命名的時(shí)候,要注意不要與系統(tǒng)命名空間中的其它全局命名對(duì)象沖突;
?。?)事件是否要自動(dòng)恢復(fù);
?。?)事件的初始狀態(tài)設(shè)置。
由于event對(duì)象屬于內(nèi)核對(duì)象,故進(jìn)程B可以調(diào)用OpenEvent函數(shù)通過對(duì)象的名字獲得進(jìn)程A中event對(duì)象的句柄,然后將這個(gè)句柄用于ResetEvent、SetEvent和WaitForMultipleObjects等函數(shù)中。此法可以實(shí)現(xiàn)一個(gè)進(jìn)程的線程控制另一進(jìn)程中線程的運(yùn)行,例如:
HANDLE hEvent=OpenEvent(EVENT_ALL_Access,true,"MyEvent"); ResetEvent(hEvent); |