多个线程在等待一个事件?

我想要的是AutoResetEvent的等效,多个线程可以等待,所有都在设置时恢复。

我知道这可以通过为每个线程设置一个AutoResetEvent并设置每个线程来实现 – 但是有更简单的方法吗? 一种不依赖于事件句柄数组的方法?

有效的(我认为)我希望能够做到这一点:

 private volatile string state; private MultiEventHandle stateChanged = new MultiEventHandle(); public void WaitForBlob() { while (true) { object saved = stateChanged.Current; // some sentinel value if (state == "Blob") break; stateChanged.WaitTilNot(saved); // wait til sentinel value != "current" } } public void SetBlob() { state = "Blob"; stateChanged.Change(); // stateChanged.Current becomes a new sentinel object } 

也就是说,任何数量的线程都可以调用WaitForBlob ,并且在任何时候(没有竞争条件) SetBlob可以被另一个线程调用,并且所有等待的线程将立即检测到更改 – 而且重要的是,没有自旋锁或Threading.Sleeps。

现在我想我可以相对容易地实现“ MultiEventHandle ”。 但我的问题是……有更好的方法吗? 当然,我认为这是错误的,因为它必须是一个非常常见的用例,但我似乎无法找到一个内置的工具来完成这项工作。 我担心我可能会在这里发明一个方形轮子。

有什么理由不使用ManualResetEvent ? 当一个等待线程通过时,这不会重置,所以它们都将被释放。

当然,这意味着如果你需要在所有等待的线程经过之后Reset事件,你需要一些方法来检测它。 您可能使用Semaphore ,但我怀疑它会很复杂。

在您设置之后,您是否需要重置事件immediatley?

我已经使用Monitor.PulseAll / Wait在幕后编写了一个可能的解决方案到了一个“WatchedVariable”类中(在这个过程中学习了一下Monitor类)。 如果其他任何人遇到同样的问题,这里发布 – 可能与不可变数据结构有关。 感谢Jon Skeet的帮助。

用法:

 private WatchedVariable state; public void WaitForBlob() { string value = state.Value; while (value != "Blob") { value = state.WaitForChange(value); } } 

执行:

 public class WatchedVariable where T : class { private volatile T value; private object valueLock = new object(); public T Value { get { return value; } set { lock (valueLock) { this.value = value; Monitor.PulseAll(valueLock); // all waiting threads will resume once we release valueLock } } } public T WaitForChange(T fromValue) { lock (valueLock) { while (true) { T nextValue = value; if (nextValue != fromValue) return nextValue; // no race condition here: PulseAll can only be reached once we hit Wait() Monitor.Wait(valueLock); // wait for a changed pulse } } } public WatchedVariable(T initValue) { value = initValue; } } 

虽然它通过了我的测试用例,但使用风险自负。

现在请咨询meta以找出我应该接受的答案。

我想出了一个不同的解决方案来解决这个问题。 它假设等待事件的线程不是WaitOne()调用的紧密循环,并且在等待调用之间有一些工作。 它使用一个AutoResetEvent ,连续调用WaitOne(0)Set()直到没有其他线程等待事件。

 // the only event we'll use: AutoResetEvent are = new AutoResetEvent(false); // starting threads: for (int i = 0; i < 10; i++) { string name = "T" + i; new Thread(() => { while (true) { are.WaitOne(); WriteLine(name); } }).Start(); } // release all threads and continue: while (!are.WaitOne(0)) are.Set(); 

上面的代码测试了1000个线程,它确实释放了它们(虽然在while循环上有太多迭代的开销,当线程中的等待调用之间有更多的工作时,可以很容易地将其限制为零。

从文档中我不清楚的一点是,Set()是否可以释放稍后在同一线程上调用的WaitOne() – 如果这种情况可能,那么这个解决方案使用起来不安全,因为它可能在退出while循环之前不释放所有线程。 如果有人能够对它有所了解,那就太好了。