如果使用setEvent设置手动重置事件但未使用ResetEvent重置,将会发生什么情况;并且该事件被触发了多次。在处理事件时,再次设置事件。
以下是示例任务:
void foo()
{
...
SetEvent(hEvent1);
...
}
void foo1()
{
...
SetEvent(hEvent2);
...
}
int MainHandler()
{
...
dwEvent = WaitForMultipleObjects(2,
ghEvents, // array of objects
FALSE, // wait for any object
5000);
switch(dwEvent)
{
case hEvent1:
//do something
break;
case hEvent2:
//do something
break;
}
}
现在,假设正在执行hEvent1的大小写(即仍设置)时,又以某种方式再次触发了hEvent1。我故意不放下ResetEvent(hEvent1),即使它是手动重置事件。那么,我们有比赛条件吗?
最佳答案
在使用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
}
这可以让您在长时间运行的后台工作过程的适当部分中对kill事件进行中间检查。
关于windows - 在没有ResetEvent的情况下调用setEvent,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/20985037/