除了用户态的线程同步,我们可以使用内核对象进行线程的同步。与用户态同步相比,内核态同步耗时要多(用户态内核态切换),但内核同步能够跨进程同步,并使用灵活,以及配套的安全机制。
触发未触发状态
对于内核对象来说,均有触发及未触发状态,其状态转换规则因不同内核对象而异。利用内核对象同步,我们就利用了内核对象的这种状态转换。
等待函数
要进行内核态同步,需要使用等待函数来使为获得等待对象触发状态的线程处于等待状态。常用的等待函数有两个:
等待单个内核对象:
~~~
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自动重置为未触发状态(等待成功的副作用)。这样,在多个等待该事件的线程中,仅有一个会被唤醒。