如果使用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),即使它是手动重置事件。那么,我们是否有竞争条件?
答案 0 :(得分:2)
事件就像布尔标志 - 可以两次赋予它真实。没有人可以等待当前发出信号的事件,所以当您将其设置为再次发出信号时没有任何反应。
我不确定“事件是否正在处理”是什么意思。看起来你用两个不同的意思使用“事件”这个词 - 一个由HANDLE
表示的内核对象,以及“我的程序必须做的事情”。
答案 1 :(得分:1)
在使用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事件进行中间检查。