The efficiency improvement of ManualResetEventSlim is achieved because when it enters a Wait() (equivalent to ManualResetEvent.WaitOne()) it initially spins in a busy loop for a short time while waiting for the event to become signalled. This StackOverflow answer explains how the spinning is done using a combination of Thread.Yield, Thread.SpinWait, Thread.Sleep(0) and Thread.Sleep(1). If the event is signalled during this period then the wait returns faster than the ManualResetEvent, which is implemented using a Monitor.Wait.
If the event doesn't occur within a certain short time then the ManualResetEventSlim switches to use a Monitor.Wait just like the ManualResetEvent. Note that the initial spinning is CPU intensive, so if you are sure that wait times will be longer you can save CPU cycles by using a ManualResetEvent and performance won't suffer.
Illustration by Example
To illustrate the potential performance improvement consider the following code. Two tasks are created which progress around a loop 1 million times in tandem as they signal each-other to continue.var numRepeats = 1000000;
var stopwatch = new Stopwatch();
var firstTask = new ManualResetEventSlim(false);
var secondTask = new ManualResetEventSlim(false);
var firstTaskComplete = new ManualResetEvent(false);
Task.Factory.StartNew(() => {
for (var x = 0; x < numRepeats; x++) {
firstTask.Wait();
firstTask.Reset();
secondTask.Set();
}
firstTaskComplete.Set();
});
Task.Factory.StartNew(() => {
for (var x = 0; x < numRepeats; x++) {
secondTask.Wait();
secondTask.Reset();
firstTask.Set();
}
});
stopwatch.Start();
firstTask.Set();
firstTaskComplete.WaitOne();
stopwatch.Stop();
Console.WriteLine("Total time: " + stopwatch.Elapsed);
Using ManualResetEventSlim as shown the above executes in about 0.52~0.55 seconds on my machine. If I switch to using ManualResetEvent then the same task takes about 11~12 times longer, 5.8~6.4 seconds.
If I leave the events signaled for the entire task without setting or resetting them, so the waits still check the manual reset event but are never blocked, then the difference between ManualResetEventSlim and ManualResetEvent is much more prominent, up to 40x.
No comments:
Post a Comment