Monitor.Wait()
and Monitor.Pulse()
are supposed to be more performant than using EventWaitHandle
derived classes such as ManualResetEvent
per this source.
In terms of performance, calling Pulse takes around a hundred
nanoseconds on a 2010-era desktop—about a third of the time it takes
to call Set on a wait handle.
This performance gain has for sure to do with how the CLR is helping out – internal data structures, sync blocks etc. Trying to understand the source code that is mostly implemented in c/c++ is difficult for me as a c# developer and the closest I managed to get to a deep-dive is from this answer which unfortunately falls a little short of providing a complete explanation:
From here we get to Object::Wait (line 531), then going to
SyncBlock::Wait (line 3442). At this point we have most of the meat of
the implementation and there’s quite a bit to it.Given all of the above and getting back to what you asked re a simpler
implementation, I’d be wary of simplifying Monitor.Wait(). There is a
lot going on under the hood and it’d be very easy to make a mistake
and have potential bugs in an alternative implementation.
In the code for SyncBlock::Wait
one can find:
CLREvent* hEvent;
...
// Before we enqueue it (and, thus, before it can be dequeued), reset the event
// that will awaken us.
hEvent->Reset();
From this answer by Hans Passant on a related question of whether Monitor.Enter() is entering kernel mode:
There’s a need to get the operating system’s thread scheduler
involved so a CLREvent is used. It is dynamically created if necessary
and its WaitOne() method is called. Which will involve a kernel
transition.
So, my questions are
-
Do
Monitor.Wait()
andMonitor.Pulse()
enter kernel mode viaCLREvent
method invocations in their implementation. (Is it even conceivably possible for them to work without OS involvement?) -
If yes, what makes them more performant than the using the thin OS handle wrappers AutoResetEvent and ManualResetEvent?