Monday 18 August 2014

AutoResetEvent and ManualResetEvent (.NET)

AutoResetEvent and ManualResetEvent facilitate synchronisation between threads by signalling when an event has occurred. A waiting thread calls the WaitOne() method which blocks while the event is in the non-signaled state. When the event is signaled from another thread then the waiting thread continues. New events are constructed with a Boolean parameter to be initially signaled or not.

The difference in the auto and manual reset events is that AutoResetEvent is automatically reset to the non-signalled state when a thread is released from a WaitOne() call. ManualResetEvent objects, on the other hand, must be reset manually.

A good analogy that is often used to describe the difference between AutoResetEvent and ManualResetEvent is that AutoResetEvent is like a turnstile that lets threads through one-at-a-time, and ManualResetEvent is like a flood gate that lets all threads through until it's closed.

To illustrate consider how results of a race could be compiled and processed in parallel by different tasks:

var newResultIn = new AutoResetEvent(false);
var allResultIn = new ManualResetEvent(false);

Task.Factory.StartNew(() => {
    while (results.Count != totalEnteries) {
        results.Add(GetNextResult());
        newResultIn.Set();
    }
    allResultIn.Set();});

Task.Factory.StartNew(() => {

    while (results.Count != totalEnteries) {
        newResultIn.WaitOne();
        UpdateLeaderBoard(results);
    }
});

Task.Factory.StartNew(() => {
    allResultIn.WaitOne();
    ProcessMaleAndFemaleWinners(results);
});

Task.Factory.StartNew(() => {
    allResultIn.WaitOne();
    ProcessAgeGroupWinners(results);
});

In this example the first task gets results and adds them to a list until all results are in. Each time a new result is added this is signaled to the second task using AutoResetEvent.Set(). The second task then continues past WaitOne(), at which point the event returns to the non-signaled state. When the leader board has been update the second task will again block on WaitOne() until the first task signals that another result is in.

When all results are in the first task then signals the ManualResetEvent allResultsIn. At this point the third and fourth tasks continue past allResultsIn.WaitOnt() and they process the results as necessary. Note that as a ManualResetEvent is used here the event remains signaled after a thread passes WaitOne(), meaning that the third and fourth tasks execute in parallel. If an AutoResetEvent had been used here then only one of the two waiting tasks would have continued.