我WaitForMultipleObjects
在 IPC 情况下使用,我有一个进程将数据写入两个内存映射文件中的一个或两个,另一个进程在数据更新时获取该数据。当任一 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 和相应的事件——但是“更新”代码对两者来说都是通用的,并且会涉及添加许多当前不必要的同步。
我还有其他选择吗?