等到 EventWaitHandle.Set() 之后通知所有进程

Posted

技术标签:

【中文标题】等到 EventWaitHandle.Set() 之后通知所有进程【英文标题】:Wait until all processes notified after EventWaitHandle.Set() 【发布时间】:2011-08-31 14:25:42 【问题描述】:

我在一个过程中

_eventWaitHandle.Set();
_eventWaitHandle.Reset();

在另一个进程中:

_eventWaitHandle.WaitOne();
Console.WriteLine("Hello");

但永远不会收到通知(没有控制台输出)。 看来 Set 是异步的。

如何等到所有服务员都收到信号后再执行 Reset()?

我创建等待句柄(NAMED 进程间等待句柄):

    internal static EventWaitHandle OpenExistingOrCreateEventWaitHandle(string name)
    
        try
        
            return EventWaitHandle.OpenExisting(name);
        
        catch (WaitHandleCannotBeOpenedException)
        
            return new EventWaitHandle(false, EventResetMode.ManualReset, name);
        
    

更新

目前我有一个“解决方案”

_eventWaitHandle.Set();
Thread.Sleep(10);
_eventWaitHandle.Reset();

第二种可能 - 每个进程有多个句柄。但此代码应适用于任何办公应用程序插件或独立应用程序。所以名称应该以某种方式生成并被发现。

第三 - 使用 WCF p2p(netPeerTcpBinding) 或带有 UdpDiscoveryEndpoint 的命名管道 - 但这些使用“IP”,因此在部署到最终用户时可能会出现一些安全问题?

【问题讨论】:

你的意思是“线程”而不是“进程” 那么这种方法对你不起作用......你不能使用EventWaitHandle在两个不同进程之间进行同步 【参考方案1】:

是的,Set() 函数会立即退出,所以像你一样调用 Set()Reset() 基本上什么都不做,或者随机做一些事情。您可以通过在WaitOne() 之后重置侦听线程上的事件来解决此问题。

【讨论】:

【参考方案2】:

您可以将EventResetMode 设置为EventResetMode.AutoReset,这样做会在其中一个进程接受事件时自动重置事件。之后您不必手动重置它。

拥有多个进程,您可以为每个侦听器创建一个事件,并在您必须发出事件信号时触发它们。

foreach(var process in _myProcesses)

    waitHandles.Add(OpenExistingOrCreateEventWaitHandle(process.SharedWaitHandleName);


...

internal static EventWaitHandle OpenExistingOrCreateEventWaitHandle(string name)

    try
    
        return EventWaitHandle.OpenExisting(name);
    
    catch (WaitHandleCannotBeOpenedException)
    
        return new EventWaitHandle(false, EventResetMode.AutoReset, name);
    



...


foreach(var waitHandle in waitHandles)

    waitHandle.Set();

【讨论】:

我可以有多个进程作为监听器。【参考方案3】:

如果您想知道如何在执行 Reset() 之前等待所有等待者都收到信号,其中等待者是同一进程中的不同线程,请查看 EventWaitHandle 类的 MSDN 页面中的此示例

using System;
using System.Threading;

public class Example

    // The EventWaitHandle used to demonstrate the difference
    // between AutoReset and ManualReset synchronization events.
    //
    private static EventWaitHandle ewh;

    // A counter to make sure all threads are started and
    // blocked before any are released. A Long is used to show
    // the use of the 64-bit Interlocked methods.
    //
    private static long threadCount = 0;

    // An AutoReset event that allows the main thread to block
    // until an exiting thread has decremented the count.
    //
    private static EventWaitHandle clearCount = 
        new EventWaitHandle(false, EventResetMode.AutoReset);

    [MTAThread]
    public static void Main()
    
        // Create an AutoReset EventWaitHandle.
        //
        ewh = new EventWaitHandle(false, EventResetMode.AutoReset);

        // Create and start five numbered threads. Use the
        // ParameterizedThreadStart delegate, so the thread
        // number can be passed as an argument to the Start 
        // method.
        for (int i = 0; i <= 4; i++)
        
            Thread t = new Thread(
                new ParameterizedThreadStart(ThreadProc)
            );
            t.Start(i);
        

        // Wait until all the threads have started and blocked.
        // When multiple threads use a 64-bit value on a 32-bit
        // system, you must access the value through the
        // Interlocked class to guarantee thread safety.
        //
        while (Interlocked.Read(ref threadCount) < 5)
        
            Thread.Sleep(500);
        

        // Release one thread each time the user presses ENTER,
        // until all threads have been released.
        //
        while (Interlocked.Read(ref threadCount) > 0)
        
            Console.WriteLine("Press ENTER to release a waiting thread.");
            Console.ReadLine();

            // SignalAndWait signals the EventWaitHandle, which
            // releases exactly one thread before resetting, 
            // because it was created with AutoReset mode. 
            // SignalAndWait then blocks on clearCount, to 
            // allow the signaled thread to decrement the count
            // before looping again.
            //
            WaitHandle.SignalAndWait(ewh, clearCount);
        
        Console.WriteLine();

        // Create a ManualReset EventWaitHandle.
        //
        ewh = new EventWaitHandle(false, EventResetMode.ManualReset);

        // Create and start five more numbered threads.
        //
        for(int i=0; i<=4; i++)
        
            Thread t = new Thread(
                new ParameterizedThreadStart(ThreadProc)
            );
            t.Start(i);
        

        // Wait until all the threads have started and blocked.
        //
        while (Interlocked.Read(ref threadCount) < 5)
        
            Thread.Sleep(500);
        

        // Because the EventWaitHandle was created with
        // ManualReset mode, signaling it releases all the
        // waiting threads.
        //
        Console.WriteLine("Press ENTER to release the waiting threads.");
        Console.ReadLine();
        ewh.Set();

    

    public static void ThreadProc(object data)
    
        int index = (int) data;

        Console.WriteLine("Thread 0 blocks.", data);
        // Increment the count of blocked threads.
        Interlocked.Increment(ref threadCount);

        // Wait on the EventWaitHandle.
        ewh.WaitOne();

        Console.WriteLine("Thread 0 exits.", data);
        // Decrement the count of blocked threads.
        Interlocked.Decrement(ref threadCount);

        // After signaling ewh, the main thread blocks on
        // clearCount until the signaled thread has 
        // decremented the count. Signal it now.
        //
        clearCount.Set();
    

【讨论】:

【参考方案4】:

我解决了问题。我使用内存映射文件来存储事件等待句柄名称的列表。超时无法稳定工作。当前的解决方案可在 2 年内投入生产。

为了获得像 IPC 桌面事件这样的 p2p,我使用了下一张收据:

1 个共享互斥锁 每个进程(事件参与者)1 个唯一的等待者和 1 个唯一的响应者事件等待句柄 1 个内存映射文件,用于存储等待参与者的注册表(可以为此使用真实注册表) 1 个用于交换事件数据的内存映射文件

【讨论】:

【参考方案5】:

使用 EventResetMode.Manual 并将您的 EventWaitHandle 存储在静态中。消费者应该从这个静态中读取句柄。每当您要调用 Set() 时,首先创建一个新的 EventWaitHandle 并将其存储在该静态中。下次消费者想要把手时,他会得到新的,这一点很清楚。

【讨论】:

以上是关于等到 EventWaitHandle.Set() 之后通知所有进程的主要内容,如果未能解决你的问题,请参考以下文章

puppeteer:如何等到元素可见?

使函数等到元素存在

等到页面加载 Selenium Python

Python 弹出命令。等到命令完成

等到播放完成

jQuery - 等到 SlideUp() 结束