除了用戶態的線程同步,我們可以使用內核對象進行線程的同步。與用戶態同步相比,內核態同步耗時要多(用戶態內核態切換),但內核同步能夠跨進程同步,并使用靈活,以及配套的安全機制。
觸發未觸發狀態
對于內核對象來說,均有觸發及未觸發狀態,其狀態轉換規則因不同內核對象而異。利用內核對象同步,我們就利用了內核對象的這種狀態轉換。
等待函數
要進行內核態同步,需要使用等待函數來使為獲得等待對象觸發狀態的線程處于等待狀態。常用的等待函數有兩個:
等待單個內核對象:
~~~
DWORD WINAPI WaitForSingleObject(
_In_ HANDLE hHandle, // 等待的內核對象句柄
_In_ DWORD dwMilliseconds // 以毫秒計數的等待超時,INFINITE為無限等待
);
~~~
該函數的返回值有以下4種
~~~
WAIT_ABANDONED
0x00000080L
The specified object is a mutex object that was not released by the thread that owned the mutex object before the owning thread terminated. Ownership of the mutex object is granted to the calling thread and the mutex state is set to nonsignaled.
If the mutex was protecting persistent state information, you should check it for consistency.
WAIT_OBJECT_0
0x00000000L
The state of the specified object is signaled.
WAIT_TIMEOUT
0x00000102L
The time-out interval elapsed, and the object's state is nonsignaled.
WAIT_FAILED
(DWORD)0xFFFFFFFF
The function has failed. To get extended error information, call GetLastError.
~~~
等待多個內核對象:
~~~
DWORD WINAPI WaitForMultipleObjects(
_In_ DWORD nCount, // 等待內核對象數目
_In_ const HANDLE *lpHandles, // 等待內核對象數組
_In_ BOOL bWaitAll, // 是否等待所有的內核對象才喚醒線程
_In_ DWORD dwMilliseconds // 等待超時時間
);
~~~
其可能返回的結果
~~~
WAIT_OBJECT_0 to (WAIT_OBJECT_0 + nCount– 1)
If bWaitAll is TRUE, the return value indicates that the state of all specified objects is signaled.
If bWaitAll is FALSE, the return value minus WAIT_OBJECT_0 indicates the lpHandles array index of the object that satisfied the wait. If more than one object became signaled during the call, this is the array index of the signaled object with the smallest index value of all the signaled objects.
WAIT_ABANDONED_0 to (WAIT_ABANDONED_0 + nCount– 1)
If bWaitAll is TRUE, the return value indicates that the state of all specified objects is signaled and at least one of the objects is an abandoned mutex object.
If bWaitAll is FALSE, the return value minus WAIT_ABANDONED_0 indicates the lpHandles array index of an abandoned mutex object that satisfied the wait. Ownership of the mutex object is granted to the calling thread, and the mutex is set to nonsignaled.
If a mutex was protecting persistent state information, you should check it for consistency.
WAIT_TIMEOUT
0x00000102L
The time-out interval elapsed and the conditions specified by the bWaitAll parameter are not satisfied.
WAIT_FAILED
(DWORD)0xFFFFFFFF
The function has failed. To get extended error information, call GetLastError.
~~~
關于等待函數有兩點應該知道的地方
1、對于一種特殊的內核對象,即mutex對象,其存在互斥量遺棄現象。即,若獲得mutex的線程終止,卻沒有顯示的釋放mutex,那么系統自動會默認原線程遺棄了mutex,并自動在等待該mutex對象的剩余線程中,挑選一個進行喚醒。而這時,wait函數的返回值不是WAIT_OBJECT(_0),而是WAIT_ABANDONED(_0)。
2、我們可以運用wait函數的擴展
[**WaitForSingleObjectEx**](https://msdn.microsoft.com/en-us/library/windows/desktop/ms687036%28v=vs.85%29.aspx)
[**WaitForMultipleObjectsEx**](https://msdn.microsoft.com/en-us/library/windows/desktop/ms687028%28v=vs.85%29.aspx)
將等待線程設置為可提醒狀態,這樣除了線程等待的內核對象外,我們可以在用戶模式下線程的異步調用隊列(APC)中,加入信號來喚醒線程(此時不要求等待內核對象是觸發的,wait函數會返回**WAIT_IO_COMPLETION**)。
類似的有[**SleepEx**](https://msdn.microsoft.com/en-us/library/windows/desktop/ms686307%28v=vs.85%29.aspx)函數。
等待成功的副作用
如果wait函數等待成功了內核對象后,會改變內核對象的狀態,這就稱作為等待成功的副作用。并不是所有的內核對象都有副作用,如進程和線程內核對象沒有任何副作用。而對于event內核對象,如果我們將event事件的重置狀態設置為自動重置,那么在一個線程獲得內核對象之前,系統會將該event自動重置為未觸發狀態(等待成功的副作用)。這樣,在多個等待該事件的線程中,僅有一個會被喚醒。