注意:當WaitForMultipleObjects等待多個內核對象的時候,如果它的bWaitAll 參數設置為false。其返回值減去WAIT_OBJECT_0 就是參數lpHandles數組的序號。如果同時有多個內核對象被觸發,這個函數返回的只是其中序號最小的那個。
如果bWaitAll 為TRUE 則等待所有信號量有效在往下執行。(FALSE 當有其中一個信號量有效時就向下執行)
問題就在這里,我們如何可以獲取所有被同時觸發的內核對象。
舉個例子:我們需要在一個線程中處理從完成端口、數據庫、和可等待定時器來的數據。一個典型的實現方法就是:用WaitForMultipleObjects等待所有的這些事件。如果完成端口,數據庫發過來的數據量非常大,可等待定時器時間也只有幾十毫秒。那么這些事件同時觸發的幾率可以說非常大,我們不希望丟棄任何一個被觸發的事件。那么如何能高效地實現這一處理呢?
MSDN中有一句非常重要的描述,它可以說是WaitForMultipleObjects用法的精髓:The function modifies the state of some types of synchronization objects. Modification occurs only for the object or objects whose signaled state caused the function to return. For example, the count of a semaphore object is decreased by one. When bWaitAll is FALSE, and multiple objects are in the signaled state, the function chooses one of the objects to satisfy the wait; the states of the objects not selected are unaffected.
多個內核對象被觸發時,WaitForMultipleObjects選擇其中序號最小的返回。而WaitForMultipleObjects它只會改變使它返回的那個內核對象的狀態。
這兒又會產生一個問題,如果序號最小的那個對象頻繁被觸發,那么序號比它大的內核對象將得不到被處理的機會。為了解決這一問題,可以采用雙WaitForMultipleObjects檢測機制來實現。見下面的例子:
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{ DWORD dwRet = 0; int nIndex = 0; while(1) { dwRet = WaitForMultipleObjects(nCount,pHandles,false,INFINITE); switch(dwRet) {case WAIT_TIMEOUT: break; case WAIT_FAILED: return 1;default:{nIndex = dwRet - WAIT_OBJECT_0; ProcessHanlde(nIndex++); //同時檢測其他的事件 while(nIndex < nCount) //nCount事件對象總數 { dwRet = WaitForMultipleObjects(nCount - nIndex,&pHandles[nIndex],false,0); switch(dwRet) {case WAIT_TIMEOUT: nIndex = nCount; //退出檢測,因為沒有被觸發的對象了. break;case WAIT_FAILED: return 1; default:{nIndex = dwRet - WAIT_OBJECT_0; ProcessHanlde(nIndex++); }break;}//switch結束}//while結束}//default結束break;}//switch結束}//while結束return 0;
}
SignalObjectAndWait()
你也可以同時通知一個內核對象,同時等待另一個內核對象,這兩個操作以原子的方式進行。
語法
DWORD SignalObjectAndWait(HANDLE hObjectToSignal,//通知的內核對象HANDLE hObjectToWaitOn,//等待的內核對象DWORD dwMilliseconds,//等待的時間BOOL bAlertable//與IO完成端口有關的參數,暫不討論
);
參數
hObjectToSignal
要發信號的對象的句柄。此對象可以是信號量,互斥量或事件。
如果句柄是信號量,則需要SEMAPHORE_MODIFY_STATE訪問權限。如果句柄是事件,則需要EVENT_MODIFY_STATE訪問權限。如果句柄是互斥鎖且調用者不擁有互斥鎖,則函數將失敗并顯示ERROR_NOT_OWNER。hObjectToWaitOn
要等待的對象的句柄。該SYNCHRONIZE訪問權是必需的; 有關更多信息,請參閱同步對象安全性和訪問權限 。有關可以指定其句柄的對象類型的列表,請參閱“備注”部分。dwMilliseconds
超時間隔,以毫秒為單位。即使對象的狀態是非信號且沒有完成或異步過程調用(APC)對象排隊,該函數也會在間隔過去時返回。如果dwMilliseconds為零,則該函數測試對象的狀態,檢查排隊的完成例程或APC,并立即返回。如果dwMilliseconds是INFINITE,則函數的超時間隔永遠不會過去。bAlertable
如果此參數為TRUE,則該函數在系統對I / O完成例程或APC函數進行排隊時返回,并且該線程調用該函數。如果為FALSE,則函數不返回,并且線程不調用完成例程或APC函數。
當排隊APC的函數調用完成時,完成例程排隊。只有當bAlertable為TRUE且調用線程是排隊APC的線程時,此函數才會返回并調用完成例程。
返回值
返回代碼/值 | 描述 |
---|---|
WAIT_ABANDONED/0x00000080L | 指定的對象是在擁有線程終止之前由擁有互斥對象的線程未釋放的互斥對象。互斥對象的所有權被授予調用線程,并且互斥鎖被設置為無信號。如果互斥鎖正在保護持久狀態信息,則應檢查它是否一致。 |
WAIT_IO_COMPLETION/0x000000C0L | 等待由一個或多個排隊到線程的用戶模式 異步過程調用(APC)結束。 |
WAIT_OBJECT_0/0x00000000L | 發出指定對象的狀態信號。 |
WAIT_TIMEOUT/0x00000102L | 超時間隔已過,對象的狀態未發出信號。 |
WAIT_FAILED/(DWORD)0xFFFFFFFF | 該功能失敗了。要獲取擴展錯誤信息,請調用 GetLastError。 |
備注
所述SignalObjectAndWait功能提供了一個更有效的方式來信號發送一個對象,然后在另一等待相比單獨的函數調用諸如 SetEvent的隨后WaitForSingleObject的。
該 SignalObjectAndWait函數可以等待以下對象:
- 更改通知
- 控制臺輸入
- 事件
- 內存資源通知
- 互斥
- 處理
- 信號
- 線
線程可以使用SignalObjectAndWait函數來確保工作線程在發信號通知對象之前處于等待狀態。例如,線程和工作線程可以使用句柄來事件對象來同步它們的工作。該線程執行如下代碼:
dwRet = WaitForSingleObject(hEventWorkerDone, INFINITE);if( WAIT_OBJECT_0 == dwRet)SetEvent(hEventMoreWorkToDo);
工作線程執行如下代碼:
dwRet = SignalObjectAndWait(hEventWorkerDone,hEventMoreWorkToDo,INFINITE, FALSE);
注意,“信號”和“等待”不能保證作為原子操作執行。在調用SignalObjectAndWait的線程開始等待第二個對象之前,在其他處理器上執行的線程可以觀察第一個對象的信號狀態。
在Windows 7中使用SignalObjectAndWait 和PulseEvent時要格外小心 ,因為在多個線程之間使用這些API會導致應用程序死鎖。由信號發送線程SignalObjectAndWait 調用PulseEvent以發信號通知的等待對象SignalObjectAndWait呼叫。在某些情況下,SignalObjectAndWait的調用者無法及時接收等待對象的信號狀態,從而導致死鎖。
使用等待函數和直接或間接創建窗口的代碼時要小心。如果一個線程創建了任何窗口,它必須處理消息。消息廣播將發送到系統中的所有窗口。使用沒有超時間隔的等待函數的線程可能會導致系統死鎖。間接創建窗口的兩個代碼示例是DDE和COM CoInitialize。因此,如果您有一個創建窗口的線程,請務必從另一個線程調用SignalObjectAndWait。如果無法做到這一點,可以使用 MsgWaitForMultipleObjects或 MsgWaitForMultipleObjectsEx,但功能不相同。
要編譯使用此函數的應用程序,請將 _WIN32_WINNT 定義為0x0400或更高版本。