ManualResetEventSlim: Calling .Set() followed immediately by .Reset() doesn\'t release any waiting threads
(Note: This also happens with
Resetting a ManualResetEvent
is not like calling Monitor.Pulse
- it makes no guarantee that it will release any particular number of threads. On the contrary, the documentation (for the underlying Win32 synchronization primitive) is pretty clear that you can't know what will happen:
Any number of waiting threads, or threads that subsequently begin wait operations for the specified event object, can be released while the object's state is signaled
The key phrase here is "any number" which includes zero.
Win32 does provide a PulseEvent
but as it says "This function is unreliable and should not be used." The remarks in its documentation at http://msdn.microsoft.com/en-us/library/windows/desktop/ms684914(v=vs.85).aspx provide some insight into why pulse-style semantics cannot reliably be achieved with an event object. (Basically, the kernel sometimes takes threads that are waiting for an event off its wait list temporarily, so it's always possible that a thread will miss a 'pulse' on an event. That's true whether you use PulseEvent
or you try to do it yourself by setting and resetting the event.)
The intended semantics of ManualResetEvent
is that it acts as a gate. The gate is open when you set it, and is closed when you reset it. If you open a gate and then quickly close it before anyone had a chance to get through the gate, you shouldn't be surprised if everyone is still on the wrong side of the gate. Only those who were alert enough to get through the gate while you held it open will get through. That's how it's meant to work, so that's why you're seeing what you see.
In particular the semantics of Set
are very much not "open gate, and ensure all waiting threads are through the gate". (And if it were to mean that, it's not obvious what the kernel should do with multi-object waits.) So this is not a "problem" in the sense that the event isn't meant to be used the way you're trying to use it, so it's functioning correctly. But it is a problem in the sense that you won't be able to use this to get the effect you're looking for. (It's a useful primitive, it's just not useful for what you're trying to do. I tend to use ManualResetEvent
exclusively for gates that are initially closed, and which get opened exactly once, and never get closed again.)
So you probably need to consider some of the other synchronization primitives.