在没有ResetEvent的情况下调用setEvent

时间:2014-01-08 00:51:09

标签: windows visual-c++ com autoresetevent manualresetevent

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

2 个答案:

答案 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事件进行中间检查。