2

如果使用 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) 即使它是手动复位事件。那么,我们有竞争条件吗?

4

2 回答 2

2

Events are like boolean flags - it's OK to assign true to it twice. No one can possibly be waiting on an event that's currently signaled, so nothing happens when you set it to signaled again.

I'm not sure what you mean by "event is getting processed". It seems you use the word "event" twice with two different meanings - a kernel object represented by a HANDLE, and "something that my program has to do".

于 2014-01-08T00:58:09.803 回答
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
}

这可以让您在长时间运行的后台工作过程的适当部分对终止事件进行中间检查。

于 2014-01-08T10:39:44.363 回答