在您使用的示例中WaitForMultipleObjects
,如果您正在等待的事件未在事件句柄数组中按频率递增的顺序列出,您可能会遇到潜在问题。另请注意我的评论,即您上面的代码假定WaitForMultipleObjects
返回事件句柄。它没有。
WaitForMultipleObjects
当看到第一个信号事件时将停止等待,从索引零向上查看数组。
因此,如果您有一个事件被设置(或未重置)作为数组中的第一个条目,那么其他事件将被饿死(即永远不会被看到)。
因此,在您的示例中,只要hEvent1
仍然发出信号,hEvent2
就不会被看到。
作为常见模式的示例,假设我们有一些工作线程,其线程函数已被路由回某个包含事件对象和互斥锁或其他内容的所属类。工作线程只响应两个事件——一个整洁地关闭的请求和一个做一些工作的请求。代码可能如下所示:
UINT CMyClass::ThreadFunc()
{
// make an array of some interesting event handles
HANDLE hEvents[2] = { m_hKillEvent, m_hWorkEvent };
// main loop - do work until killed
while (true)
{
// wait for either event
DWORD dwRet = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE);
// see why we got signalled
if (dwRet == WAIT_OBJECT_0)
{
// kill requested - exit loop
break;
}
else if (dwRet == WAIT_OBJECT_0 + 1)
{
// work requested - do some work here
}
else
{
// error handling - bad handles?
}
}
// normal exit
return 0;
}
按照编码,这将正常工作 - 主线程调用SetEvent(m_hWorkEvent)
触发后台线程做一些工作,它调用SetEvent(m_hKillEvent)
使工作线程关闭。如果线程在工作中,关闭可能会受到一些超时保护,例如:
// close worker thread
SetEvent(m_hKillEvent);
// wait for thread to die peacefully
DWORD dwRet = WaitForSingleObject(m_hWorkerThread, 5000);
if (dwRet == WAIT_TIMEOUT)
{
// worker failed to respond - error handling here
}
现在,即使m_hWorkEvent
非常频繁地发出信号,这个关闭过程也可以正常工作——例如,当时间do some work here
结束时,事件又被发出了信号。这是因为WaitForMultipleObjects
将始终首先检查 kill 事件,因为它是数组中的第一个。
但是,如果数组是这样定义的:
// make an array of some interesting event handles
HANDLE hEvents[2] = { m_hWorkEvent, m_hKillEvent };
如果m_hWorkEvent
不断发出信号(例如,它在长时间运行期间再次设置do some work here
,或者它是手动重置事件并且您永远不会重置它),那么线程将永远不会干净地退出,因为它永远不会看到终止信号。它总是会先尝试做一些工作。
这就是我所说的以频率递增的顺序对数组中的事件进行排序。kill 事件的频率最低(它只发出一次信号),所以它首先发生。如果您有三个或更多事件用于不同的工作请求,则需要保持相同的顺序,否则某些事件会被饿死。
无论您决定做什么,同样值得注意的是,即使WaitForMultipleObjects
在“错误”事件上得到释放,您仍然可以通过等待零超时来检查特定事件是否发出信号:
if (WaitForSingleObject(hSomeEvent, 0) == WAIT_OBJECT_0)
{
// ... hSomeEvent was signaled
}
这可以让您在长时间运行的后台工作过程的适当部分对终止事件进行中间检查。