English 中文(简体)
适当通知系统范围内命名的手动重置事件的所有听众,然后立即重置它。
原标题:
  • 时间:2009-01-09 20:52:38
  •  标签:

我通过以下步骤创建一个系统级手动重置事件:

EventWaitHandle notifyEvent = new EventWaitHandle(false, EventResetMode.ManualReset, notifyEventName, out createdEvent);

多个流程都会引起此事件(例如它们之间是共享的)。它用于通知更新情况。

我想能够设置此事件,以便等待它的所有进程都被发出信号,然后立即重置它,以使对事件的后续等待被阻止。

如果我做一个

notifyEvent.Set();
notifyEvent.Reset();

有时它会通知所有正在监听的进程。

如果我做一个

notifyEvent.Set();
Thread.Sleep(0);
notifyEvent.Reset();

更多的进程会收到通知(我假设这会发生,因为调度程序有机会运行)。

如果我这样做

notifyEvent.Set();
Thread.Sleep(100);
notifyEvent.Reset();

然后一切似乎都运作良好,所有进程(例如~8)都得到了一致的通知。我不喜欢在睡眠调用中使用“魔数”。

有没有更好的方法通知其他进程中的所有监听器操作系统事件已发生,以便在通知时所有正在监听事件的人都会接收事件信号,然后立即重置事件,以便任何其他要监听事件的人都将被阻塞?

更新:由于事件监听器数量随时间变化,信号量似乎不适合此处。在需要通知事件时,提前不知道有多少个监听器。

最佳回答

你正在错误地使用EventWaitHandle类。重置事件不应该用于信号多个线程。相反,你需要为每个线程创建一个重置事件,当你准备好时遍历它们并使用Set()。主线程不应该调用Reset()方法。每个线程都应该负责将门关闭在他们后面,可以这么说。

这里是一个基本的例子:

static class Program
{
    static void Main()
    {
        List<ThreadState> states = new List<ThreadState>();
        ThreadState myState;
        Thread myThread;
        string data = "";

        for (int i = 0; i < 4; i++)
        {
            myThread = new Thread(Work);
            myState = new ThreadState();
            myState.gate = new EventWaitHandle(false, EventResetMode.ManualReset);
            myState.running = true;
            myState.index = i + 1;
            states.Add(myState);
            myThread.Start(myState);
        }

        Console.WriteLine("Enter q to quit.");

        while (data != "q")
        {
            data = Console.ReadLine();
            if (data != "q")
                foreach (ThreadState state in states)
                    state.gate.Set();
        }

        foreach (ThreadState state in states)
        {
            state.running = false;
            state.gate.Set();
        }

        Console.WriteLine("Press any key to quit.");
        Console.ReadKey();
    }

    static void Work(Object param)
    {
        ThreadState state = (ThreadState)param;
        while (state.running)
        {
            Console.WriteLine("Thread #" + state.index + " waiting...");
            state.gate.WaitOne();
            Console.WriteLine("Thread #" + state.index + " gate opened.");
            state.gate.Reset();
            Console.WriteLine("Thread #" + state.index + " gate closed.");
        }
        Console.WriteLine("Thread #" + state.index + " terminating.");
    }

    private class ThreadState
    {
        public int index;
        public EventWaitHandle gate;
        public bool running;
    }
}
问题回答

我遇到了同样的问题,惊讶地发现在解决这种松耦合/发射和忘记/多个监听器类型事件的问题上,网上找不到好的解决方案,所以我想出了这个方案。

请注意,使用 Set() Reset()调用之间的超时解决方案也存在竞争条件问题(除了它依赖于任意超时值的事实之外):如果发布者在这些调用之间被杀死,则所有侦听器将永远看到事件被设置为(除非发布者再次上线)。

因此要求是:

  • there is one publisher (although it s not really enforced in the code)
  • there can be any number of listeners (in the same process or in other processes), between 0 and N (N is fixed once the binaries are compiled).
  • listeners can come and go as they want without disturbing the publisher
  • publisher can come and go as it wants without disturbing the listeners

秘诀是使用AutoReset事件,因为它们没有竞争条件问题,但为每个侦听器定义一个。我们事先不知道侦听器数量,但可以固定最大侦听器数量(N如上所述)。

const int MAX_EVENT_LISTENERS = 10;
const string EVENT_NAME = "myEvent_";

这是发行者代码,将事件提高到所有潜在听众:

public static void RaiseEvent()
{
    for (int i = 0; i < MAX_EVENT_LISTENERS; i++)
    {
        EventWaitHandle evt;
        if (EventWaitHandle.TryOpenExisting(EVENT_NAME + i, out evt))
        {
            evt.Set();
            evt.Dispose();
        }
    }
}

这里是获取事件通知的听众代码:

...
EventWaitHandle evt = GetEvent();
do
{
    bool b = evt.WaitOne();
    // event was set!
}
while (true);
....

// create our own event that no other listener has
public static EventWaitHandle GetEvent()
{
    for (int i = 0; i < MAX_EVENT_LISTENERS; i++)
    {
        bool createdNew;
        EventWaitHandle evt = new EventWaitHandle(false, EventResetMode.AutoReset, EVENT_NAME + i, out createdNew);
        if (createdNew)
            return evt;

        evt.Dispose();
    }
    throw new Exception("Increase MAX_EVENT_LISTENERS");
}

您在这里使用了错误的同步类型。您应该使用Semaphore类,指定您希望允许的同时访问数,而不是事件。

你可能还想要有两个信号量,第二个用于触发事件的代码检查(响应事件的代码会持有锁),以防想要两个连续事件,并且一段代码进入另一个事件尾巴。





相关问题
热门标签