如何使用WaitForMultipleObjects来平衡竞争工作?

时间:2013-05-30 07:21:19

标签: c++ windows multithreading winapi visual-c++

我在IPC情况下使用WaitForMultipleObjects,我有一个进程将数据写入两个内存映射文件中的一个或两个,另一个进程在更新时获取该数据。当任一MMF中的数据发生变化时,我正在使用命名事件对象来通知第二个进程。还有一个终止“观察者”线程的事件。

所以代码的简化示例是这样的(编辑 - 注意事件对象已被创建为自动重置事件

unsigned int CMyClass::ThreadFunc()
{
    // background thread
    HANDLE hEvent[3];

    // open events for updates 0 and 1 and kill signal
    hEvent[0] = ::OpenEvent(SYNCHRONIZE, FALSE, _T("KillEvent"));
    hEvent[1] = ::OpenEvent(SYNCHRONIZE, FALSE, _T("UpdateEvent0"));
    hEvent[2] = ::OpenEvent(SYNCHRONIZE, FALSE, _T("UpdateEvent1"));

    // main loop
    while (true)
    {
        // wait for any event and break on kill signal
        DWORD dwRet = WaitForMultipleObjects(3, hEvent, FALSE, INFINITE);
        if (dwRet == WAIT_OBJECT_0) break;

        // which update event did we get?
        if (dwRet == WAIT_OBJECT_0 + 1) 
        {
            // perform update from channel 0
        }
        else if (dwRet == WAIT_OBJECT_0 + 2)  
        {
            // perform update from channel 1
        }
    }

    // release handles 
    for (int i = 0; i < 3; ++i)
        CloseHandle(hEvent[i]);

    // exit thread
    return 0;
}

在最常见的用例中,只更新了一个MMF,因此此代码可以正常工作。但是,当两个 MMF正在更新时,我发出两个事件信号,我注意到通过记录和调试第一个事件的处理大约是第二个事件的两倍 - 即使进程正在执行更新只是在相邻代码行中的每个上调用SetEvent。这使得一次更新的外观比另一次更慢,因此来自用户的错误报告。

仔细观察MSDN,它表明了为什么会发生这种情况

  

如果多个对象发出信号,该函数将返回其对象已发出信号的数组中第一个句柄的索引。

所以看起来第二个事件只是打破等待,如果上面代码中的处理设法完成之前在第一个事件上调用另一个SetEvent之前执行

因此,要暂时解决问题,我只是单方面执行两个更新,无论设置了哪个事件。

        // wait for any event
        DWORD dwRet = WaitForMultipleObjects(3, hEvent, FALSE, INFINITE);
        if (dwRet == WAIT_OBJECT_0) break;

        // perform update from channel 0

        // perform update from channel 1

这显然不太理想,而且非常浪费,因为就像我上面所说的那样,对于最常见的用例,只有一个 MMF正在更新。

处理此类情况的最佳方法是什么?我考虑使用两个线程 - 每个MMF和相应的事件一个 - 但“更新”代码对两者都是通用的,并且将涉及添加当前不必要的大量同步。

我还有其他选择吗?

2 个答案:

答案 0 :(得分:4)

处理完一个事件后,您可以重新排列下一次调用中传递给WaitForMultipleObjects的句柄数组。因此,完成事件1会使事件2成为下一次的优先事件。反之亦然。

答案 1 :(得分:2)

如果WaitForMultipleObjects的返回值为WAIT_OBJECT_1,那么您仍然可以使用WaitForSingleObject检查MMF 2的事件是否已设置。

DWORD dwRet = WaitForMultipleObjects(3, hEvent, FALSE, INFINITE);

if (dwRet == WAIT_OBJECT_0) break;

// decide which MMFs need processing
if ( dwRet == WAIT_OBJECT_1 )
{
    if ( WaitForSingleObject( hEvent[2], 0 ) == WAIT_OBJECT_0 )
        // both MMFs have been updated, decide which to do first.
    else
        // only MMF 1 has been updated, process that
}
else if ( dwRet == WAIT_OBJECT_2 )
{
    // only MMF 2 has been updated as WaitForMultipleObjects returns 
    // lowest index of set event.
}

// do processing