【问题标题】:How do I use WaitForMultipleObjects to balance competing work?如何使用 WaitForMultipleObjects 来平衡竞争性工作?
【发布时间】:2013-05-25 16:16:06
【问题描述】:

我在 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 和相应的事件——但是“更新”代码对两者都是通用的,并且会涉及添加许多当前不必要的同步。

我还有其他选择吗?

【问题讨论】:

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


    【解决方案1】:

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

    【讨论】:

    • 现在这是个好主意 - 我会看看它如何与实际代码相匹配 - 非常感谢。
    【解决方案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
    

    【讨论】:

    • 谢谢,我确实尝试过类似的事情,但问题是这些是自动重置事件,所以当原始等待被打破时,再次等待零时间会错过原始事件。
    • @RogerRowland 这是非常重要的信息!我已经更新了自动重置事件的答案。
    • 好点 - 我已经编辑了问题以明确这一点,抱歉。
    猜你喜欢
    • 2012-06-19
    • 1970-01-01
    • 2011-05-22
    • 2023-03-25
    • 2019-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多