单个 SetEvent() 能否触发多个 WaitForSingleObject()

Posted

技术标签:

【中文标题】单个 SetEvent() 能否触发多个 WaitForSingleObject()【英文标题】:Can a single SetEvent() trigger multiple WaitForSingleObject() 【发布时间】:2009-03-07 11:18:21 【问题描述】:

这个:

http://msdn.microsoft.com/en-us/library/ms686915(VS.85).aspx

似乎不建议这样做。

我有三个通过管道进行通信的进程。进程 A 创建一个事件,进程 B 和 C 各自使用 WaitForSingleObject(在第二个线程中)。

所以现在我们有 -TWO- 进程,每个进程都在等待 -SINGLE- 事件。​​

进程 A 使用 SetEvent() 触发事件,进程 B 响应,进程 C 不响应。

结论:

每个 WaitForSingleObject() 都需要一个唯一的事件...对吗?

【问题讨论】:

【参考方案1】:

使用手动重置事件触发单个事件的多个线程。

Here 是一个使用“手动重置事件”标志的示例

【讨论】:

不幸的是我无法控制进程 A,所以我无法实现。【参考方案2】:

希望这个例子可以帮助到你:

handle1A = CreateEvent(LPSECURITY_ATTRIBUTES, ManualReset, InitialState, NAME)

handle1B = CreateEvent(LPSECURITY_ATTRIBUTES, ManualReset, InitialState, NAME)

handle2A = CreateEvent(LPSECURITY_ATTRIBUTES, ManualReset, InitialState, NAME+GetCurrentThreadId())

handle2B = CreateEvent(LPSECURITY_ATTRIBUTES, ManualReset, InitialState, NAME+GetCurrentThreadId())

A) 如果您使用相同的NAME 创建一个事件,则每个setEvent 都会发出所有waitforsingleobjects 的信号

SetEvent(handle1A) // Send signaling to handle1A and handle1B

B) 如果您创建一个具有唯一 NAME 的事件,setEvent 只会将信号发送到引用的句柄

SetEvent(handle2) // Send signling only to handle2A. The Id Thread is unique

【讨论】:

【参考方案3】:

如果是手动重置事件,一个事件可以通知多个线程。自动重置事件无法做到这一点。如果多个线程同时等待自动重置事件,并且您将其设置为信号状态,则只有一个线程存在并重置它,其他线程的行为将是未定义的。虽然,从 Microsoft 文档中,我们可以假设只有一个线程会退出,而其他线程肯定不会退出。无论如何,我们必须考虑以下引用:“不要假设先进先出 (FIFO) 顺序。内核模式 APC 等外部事件可以改变等待顺序” 来源 - https://msdn.microsoft.com/en-us/library/windows/desktop/ms682655(v=vs.85).aspx

CreateEvent 函数具有 bManualReset 参数。如果为 TRUE,则该函数创建一个手动重置事件对象,该对象需要使用 ResetEvent 函数将事件状态设置为未发出信号。如果此参数为 FALSE,则该函数创建一个自动重置事件对象,并且系统会在单个等待线程被释放后自动将事件状态重置为非信号状态,即已从 WaitForMultipleObjects 或 WaitForSigleObject 等函数退出 - 但是,如我之前写过,不会全部通知一个线程。

关于 PulseEvent – 它不可靠,永远不应该使用 – 请参阅 https://msdn.microsoft.com/en-us/library/windows/desktop/ms684914(v=vs.85).aspx

只有在调用 PulseEvent 时处于“等待”状态的线程才会被 PulseEvent 通知。如果它们处于任何其他状态,它们将不会收到通知,并且您可能永远无法确定线程状态是什么。等待同步对象的线程可以通过内核模式异步过程调用暂时从等待状态中移除,然后在 APC 完成后返回等待状态。如果对 PulseEvent 的调用发生在线程已从等待状态中移除的时间内,则不会释放线程,因为 PulseEvent 仅释放在调用它时正在等待的那些线程。您可以在以下链接中找到有关内核模式异步过程调用 (APC) 的更多信息:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms681951(v=vs.85).aspx http://www.drdobbs.com/inside-nts-asynchronous-procedure-call/184416590 http://www.osronline.com/article.cfm?id=75

您可以从以下文章中获得有关自动重置事件和手动重置事件的更多想法:

https://www.codeproject.com/Articles/39040/Auto-and-Manual-Reset-Events-Revisited

【讨论】:

【参考方案4】:

您可以使用手动重置事件和 PulseEvent 函数来释放当前等待事件的所有线程。

但是请注意,这种方法本质上是活泼的,因为无法分辨哪些是“当前正在等待的线程......”。如果需要精确匹配 wakeup/2 等待事件,您应该使用更可靠的同步机制。

【讨论】:

我没有提到这一点,但我对进程 A 没有任何控制权,但我会查看 PulseEvent 函数以供将来参考.. 谢谢。 如果您只能更改 B 和 C 线程,最好从另一个线程(即 A -> B -> C)唤醒一个线程,而不是尝试从 A 唤醒所有线程。顺便说一句,如果你不控制 A,你应该有一些锁定/唤醒协议/接口,并在问题中提到这一点。 不要使用PulseEvent,见blogs.msdn.com/oldnewthing/archive/2005/01/05/346888.aspx @CesarB:这可能是真的,但在某些情况下,不唤醒所有线程并不重要。例如,在线程等待数据的图像/音频处理情况下。在最坏的情况下,一些线程只会错过一些并不重要的帧。【参考方案5】:

“很遗憾,我无法控制进程 A,所以我无法实现它。”

如果进程 A 创建一个自动重置事件。或许你可以在进程b中使用SetEvent再次触发,让进程c执行。

【讨论】:

以上是关于单个 SetEvent() 能否触发多个 WaitForSingleObject()的主要内容,如果未能解决你的问题,请参考以下文章

[C++][linux]C++实现类似C#AutoResetEvent或者win C++的SetEvent

mediafoundation 能否允许多个客户端同时访问单个网络摄像头设备?

我能否为单个 UI 元素提供多个辅助功能 ID?

我们能否从单个devops存储库为多个Web应用程序创建多个部署

使用多个 Azure 队列触发单个 Azure 函数

定时调度框架quartz可以定义单个触发器多个任务吗