Multiple threads waiting for a single event?

What (I think) I want is the equivalent of an AutoResetEvent that can wait for multiple threads, and all of this will resume when it is installed.

I know this can be achieved by having an AutoResetEvent for each thread and setting each one - but is there an easier way? A way that doesn't depend on event descriptor arrays?

Effectively, that (I think), I would like this to be possible:

 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 } 

that is, any number of threads can call WaitForBlob , and at any time (without race conditions) SetBlob can be called by another thread, and all waiting threads will immediately detect a change, and, importantly, without spin locks or Threading.Sleeps.

Now I think that I can implement " MultiEventHandle " relatively easily. But my question is ... is there a better way? Of course, I'm going to do it wrong, as this should be a fairly common use case, but I cannot find a built-in tool to work. I'm afraid I'm probably going to come up with a square wheel here.

+4
source share
3 answers

I wrapped a possible solution in the WatchedVariable class using Monitor.PulseAll / Wait backstage (learning a little about the Monitor class in the process). Posting here, if anyone else encounters the same problem, can be useful with immutable data structures. Thanks to Jon Skeet for the help.

Using:

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

Implementation:

 public class WatchedVariable<T> 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; } } 

While he passed my test cases, use at your own risk.

Now consult a meta to find out which answer I should accept.

+2
source

Any reason not to use ManualResetEvent ? This will not reset when one waiting thread passes, so they will all be released.

Of course, this means that if you need a Reset event after all pending threads have passed, you will need to somehow detect this. You can use Semaphore instead, but I suspect it will be difficult.

Do you need to reset the event immediately after you set it, in your case?

+2
source

I came up with another solution to this problem. It is assumed that the threads waiting for events are not hard WaitOne() call WaitOne() , and there is some work between wait calls. It uses one AutoResetEvent , calls WaitOne(0) and Set() continuously, until other threads expect an event.

 // 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(); 

The above code is tested for 1000 threads, it released them all (although there are too many iterations in the while loop, which can easily be limited to zero when there is still a bit of work between waiting calls to threads.

Something that is not clear to me from the documentation is whether it is possible for Set () to issue WaitOne (), which was called later in the same thread - if this situation is possible, this solution is unsafe to use since it may not free all threads before exiting the while loop. It would be nice if someone could shed light on him.

0
source

Source: https://habr.com/ru/post/1345052/


All Articles