【事件】
事件用處多是控制線程間的同步。
最典型的應用就是CreateThread之後等待線程函數的啟動。如Main線程裡CreateThread,它之後的操作依賴於子線程,那麼它一般會在CreateThread之後判斷HANDLE是否有效,然後進入等待。(當然在這之前,一個Event是已經創建好的,並初始化為未通知狀態)子線程啟動後完成了初始化操作,並設置Event為已通知狀態。這時,一直在等待該事件的Main線程發現該事件已經得到通知,因此它就變成可調度線程。這時Main線程知道子線程已經完成了初始化操作。
CreateEvent函數用於創建一個Event,其原型如下:
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset,
BOOL bInitialState,
LPTSTR lpName
); 參數說明:
- 第一個參數同CreateThread類似,也是安全級別相關,通常被被設置為NULL,以獲得默認的安全級別。
- 第二個參數是個布爾值,它能夠告訴系統是創建一個人工重置的事件(TRUE)還是創建一個自動重置的事件( FALSE)。
- 第三個參數也是布爾值,用於指明該事件是要初始化為已通知狀態(TRUE)還是未通知狀態(FALSE)。
- 第四個參數是一個字符串,用於標示這個事件的名字。
以下是詳細說明:
- 已通知狀態和未通知狀態
事件只有兩種狀態,已通知表示這個事件已經被設置過了(可以理解為發生了),未通知表示還沒有發生。一般設置為未通知狀態,並由SetEvent設置為已通知狀態。當然也可以反著做,CreateEvent時設置為已通知狀態,然後由ResetEvent設置為未通知狀態。
- 人工重置與自動重置
自動重置的事件定義了應該成功等待的副作用規則,即當線程成功地等待到該對象時,自動重置的事件就會自動重置到未通知狀態。
人工重置則需要調用ResetEvent函數設置為未通知狀態。
- 名字共享
這個參數很重要,Win32 API中有很多方法有這個參數,它遵從一種按名字共享的規則。
如果傳入一個非NULL字符串(最多260個字符),那麼在全局空間,共享該HANDLE,這個全局可以是跨進程的名字空間,即在另一個進程中依然能夠使用該名字的HANDLE。
如果希望避免這種全局范圍內的共享,那麼應該傳入NULL,以一種匿名的方式創建Event等,這樣,它只在當前線程內可見。
當進程A創建了一個Event後,如CreateEvent(NULL,FALSE,FLASE,_T(“UniqueEvent”));進程B同樣創建了一個Event,也想起名字為UniqueEvent,那麼就會出現問題:CreateEvent(NULL,FALSE,FALSE,_T(“UniqueEvent”));系統會首先查看是否已經存在了一個名字為“UniqueEvent”的對象,由於確實存在了一個帶有改名字的內核對象,因此內核要檢查對象類型,同樣是一 個Event,那麼系統會執行一次安全檢查,以確定調用者是否擁有對該對象的完整訪問權。如果有這種訪問權,系統會在進程B的句柄表裡找到一個空項目,對 其初始化,使得該項指向現有的內核對象。如果類型不匹配,或者拒絕訪問,那麼進程B的CreateEvent會失敗。
應用程序能夠確定它是否確實創建了一個新內核對象,而不是打開了一個現有的對象。方法是在調用C r e a t e *函數後立即調用G e t L a s t E r r o r:如果為ERROR_ALREADY_EXISTS,那麼表示系統內已經存在了這樣名字的對象。
Open*是去查看名字空間中是否有這個名字的內核對象存在調用C r e a t e *函數與調用O p e n *函數之間的主要差別是,如果對象並不存在,那麼C r e a t e *函數將創建該對象,而O p e n *函數則運行失敗。
PulseEvent函數使得事件變為已通知狀態,然後立即又變為未通知狀態,這就像在調用SetEvent後又立即調用ResetEvent函數一樣。如果在人工重置的事件上調用PulseEvent函數,那麼在發出該事件時,等待該事件的任何一個線程或所有線程將變為可調度線程。如果在自動重置事件上調用P u l s e E v e n t函數,那麼只有一個等待該事件的線程變為可調度線程。如果在發出事件時沒有任何線程在等待該事件,那麼將不起任何作用。
【等待函數】
等待函數用來監聽事件的已通知狀態。WaitForSingleObject和WaitForMultipleObjects兩個函數分別用以等待單個事件和多個事件。
DWord WaitForSingleObject(
HANDLE hHandle,
DWord dwMilliseconds
);
DWord WaitForMultipleObjects(
DWord nCount,
const HANDLE* lpHandles,
BOOL bWaitAll,
DWord dwMilliseconds
); 從函數原型上來看可知,事件的含義是能夠支持被通知/未通知的內核對象(例如進程和線程,當傳入的是進程或者線程句柄時,他表示等該線程或進程被標識為終止運行為止。)。
dwMilliseconds參數表明等待的時間,如果在這個時間段中事件為已通知狀態,那麼對於Single版本將返回WAIT_OBJECT_0,對於Multiple版本將返回WAIT_OBJECT_0 to (WAIT_OBJECT_0 + nCount– 1)。如果沒有等到將返回WAIT_TIMEOUT。
Multiple版本中的bWaitAll表示想要讓它使用何種方式等待。如果為該參數傳遞TRUE,那麼在所有對象變為已通知狀態之前,該函數將不允許調用線程運行。一般是FALSE,即只要有一個事件被相應,則線程可調度。