我有一个系统范围的手动重置事件,我通过执行以下操作创build:
EventWaitHandle notifyEvent = new EventWaitHandle(false, EventResetMode.ManualReset, notifyEventName, out createdEvent);
几个进程创build这个事件(例如它们之间共享)。 它用于通知何时更新。
我希望能够设置此事件,以便等待的所有进程都被发送信号,然后立即将其重置,以便事件的后续等待被阻止。
如果我做了
notifyEvent.Set(); notifyEvent.Reset();
它有时会通知所有的聆听程序。
如果我做了
notifyEvent.Set(); Thread.Sleep(0); notifyEvent.Reset();
更多的进程得到通知(我认为这会发生,因为调度程序有机会运行)。
如果我这样做
notifyEvent.Set(); Thread.Sleep(100); notifyEvent.Reset();
然后,一切似乎工作正常,所有进程(如〜8)得到一致通知。 我不喜欢睡眠呼叫使用“幻数”。
是否有更好的方法来通知所有监听器在其他进程中的事件已经发生,以便每个在通知时收听事件的人收到事件信号,然后立即重置事件,以便任何其他人去听到事件会阻止?
更新:信号量在这里似乎不太合适,因为事件监听器的数量可能会随时间而变化。 事先并不知道甚至需要通知有多less听众。
您正在使用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()
调用之间的超时解决方案也存在争用条件问题(超出了它依赖于任意超时值的事实):如果发布者在这些调用之间中断,则所有的监听器都会看到这个事件永远是永久性的(除非发布者重新开始)。
所以要求是:
诀窍是使用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类,而不是一个事件,同时访问您希望允许的数量。
您可能还想要两个信号量,第二个是用于启动事件检查的代码(响应事件的代码将持有锁),以防您不想快速连续发生两个事件,有一段代码进入另一个事件的尾巴。