線程同步之等待函數和事件內核對象

 

用內核對象進行線程同步

內核對象:Windows操做系統使用內核對象來管理進程、線程、文件等諸多種類的大量資源。內核對象的建立一般是經過Windows API,好比CreateThread將建立一個線程內核對象,並返回一個內核對象句柄。內核對象其實是一小塊內存,其中包括了引用計數、安全性描述等信息,操做系統經過這一小段內存來管理對應的內核資源。內核對象的實際內存地址並不是句柄所展現的,它們在進程內的內核對象句柄表中有映射。html

在前幾篇中,介紹了在用戶模式下的線程同步機制:www.189works.com InterLocked系列、關鍵段、Slim讀寫鎖。這些同步機制能夠在進行線程同步的同時讓線程保持在用戶模式下。然而用戶模式下的線程同步機制有時不能知足咱們的要求。從這篇開始,將介紹使用內核對象進行線程同步。在考慮是使用用戶模式的同步機制仍是使用內核對象來同步的時候,須要綜合考量,儘可能使用用戶模式的線程同步機制。windows

內核對象廣泛存在兩種狀態,要麼是觸發,要麼是未觸發。每種內核對象在這兩個狀態間切換過程都有其特殊的特色,好比進程內核對象在建立的時候老是處於未觸發狀態,當進程終止時,會變成觸發狀態;並且進程內核對象永遠不會從觸發態變回未觸發態。因而,若是咱們的線程須要等待子進程終止時才繼續,那麼能夠將線程進入等待狀態,當子進程標識的進程內核對象變成觸發狀態的時候喚醒線程!咱們所須要的僅僅是Windows爲咱們提供的等待函數。安全

 

等待函數

等待函數可以是一個線程進入等待狀態,直到指定的內核對象被觸發爲止。這些等待函數有:ide

DWORD WINAPI WaitForSingleObject(  __in  HANDLE hHandle,  __in  DWORD dwMilliseconds);

當線程調用WaitForSingleObject的時候,第一個參數hObject用來標識內核對象,第二個參數是個超時時間(傳入INFINITE表示永遠等待直到觸發)。函數

DWORD WINAPI WaitForMultipleObjects(  __in  DWORD nCount,  __in  const HANDLE *lpHandles,  __in  BOOL bWaitAll,  __in  DWORD dwMilliseconds);www.189works.com

WaitForMultipleObjects容許調用線程檢查多個內核對象的觸發狀態。spa

使用等待函數有時是會改變內核對象的狀態的。好比:線程正在等待一個自動重置事件對象,當事件對象被觸發的時候,函數會檢測到這一狀況並返回調用線程,可是在返回以前,他會使事件變爲非觸發狀態。等待函數在不一樣的內核對象上調用並返回所引發的這樣相似的「反作用」是不一樣的。在對內核對象展開介紹後,將看到這一點。操作系統

若是多個線程在同時等待同一個內核對象,那麼任何一個線程都有可能被喚醒。咱們不該該作出相似「誰先等待誰就先喚醒」這樣的假設。線程

接下來,將分幾篇的內容分別介紹那些與線程同步有關的內核對象。code

 

事件內核對象

事件內核對象分爲手動重置和自動重置,區別在於當對象從未觸發狀態變成觸發狀態後,會不會自動重置回未觸發狀態。與其餘內核對象相同,事件內核對象也包括引用計數。下面的函數CreateEvent用以建立事件內核對象:orm

HANDLE CreateEvent(  LPSECURITY_ATTRIBUTES lpEventAttributes,   BOOL bManualReset,   BOOL bInitialState,   LPTSTR lpName ); 

lpEventAttributes:會被忽略,必須爲NULL(這裏是MSDN的說法,在Windows Via C/C++中,暗示了這個參數與內核對象的安全屬性和共享特性有關。不過一般都會傳入NULL,由於不太會考慮讓一個事件對象變爲可繼承)

bManualReset:手動重置(TRUE)仍是自動重置(FALSE)

bInitialState:初始狀態爲觸發(TRUE)仍是非觸發(FALSE)

lpName:用於共享內核對象的機制,這裏與線程同步無關,再也不闡述,傳入NULL便可。

返回值:事件對象的句柄

Windows Vista還支持下面這個函數CreateEventEx來建立事件內核對象,詳情請參見MSDN:

HANDLE WINAPI CreateEventEx(  __in_opt  LPSECURITY_ATTRIBUTES lpEventAttributes,  __in_opt  LPCTSTR lpName,  __in      DWORD dwFlags,  __in      DWORD dwDesiredAccess);

 

咱們使用下面的函數來控制事件對象的狀態:

BOOL SetEvent(  HANDLE hEvent ); //將內核對象設置爲觸發狀態BOOL ResetEvent(   HANDLE hEvent );//將內核對象設置爲非觸發狀態BOOL PulseEvent(  HANDLE hEvent ); //觸發內核對象並當即將其重置爲未觸發狀態,會喚醒正在等待的線程

若是用圖來表示他們的做用就很直觀了:

p_w_picpath

另外OpenEvent一般用於進程同步,但前提是事件內核對象必須有別名(經過給某些內核對象起別名,是一種共享內核對象的方式):

HANDLE OpenEvent(   DWORD dwDesiredAccess,   BOOL bInheritHandle,   LPCTSTR lpName ); 

 

下圖描述了事件內核對象的示例用法(同一進程內):

p_w_picpath

主線程先建立一個事件內核對象,並建立兩個線程,這兩個線程的執行須要依賴主線程的準備工做,所以調用WaitForSingleObject等待事件對象觸發。主線程完成準備工做後,調用SetEvent使對象變成觸發狀態,這時兩個子線程將被喚醒開始執行代碼。

在後面的篇章中,將繼續介紹其餘能夠用來線程同步的內核對象。

相關文章
相關標籤/搜索