views:

583

answers:

1

Hi

I was under the impression, after reading this article http://www.yoda.arachsys.com/csharp/threads/ that it is better to use Monitor/Lock for thread synchronisation as it does not use native resources

Specific quote (from page 5 of the article):

Monitor.Wait/Pulse isn't the only way of waiting for something to happen in one thread and telling that thread that it's happened in another. Win32 programmers have been using various other mechanisms for a long time, and these are exposed by the AutoResetEvent, ManualResetEvent and Mutex classes, all of which derive from WaitHandle. All of these classes are in the System.Threading namespace. (The Win32 Semaphore mechanism does not have a managed wrapper in .NET 1.1. It's present in .NET 2.0, but if you need to use it before then, you could either wrap it yourself using P/Invoke, or write your own counting semaphore class.)

Some people may be surprised to learn that using these classes can be significantly slower than using the various Monitor methods. I believe this is because going "out" of managed code into native Win32 calls and back "in" again is expensive compared with the entirely managed view of things which Monitor provides. A reader has also explained that monitors are implemented in user mode, whereas using wait handles require switching into kernel mode, which is fairly expensive.

But since discovering SO and reading a few of the questions/answers here I have started to doubt my understanding of when to use each. It seems that many people recommend using Auto/ManualResetEvent in the cases where a Monitor.Wait/Pulse would do. Can anyone explain to me when WaitHandle based sync should be used over Monitor?

Thanks

+11  A: 

A problem with Monitor.Pulse/Wait is that the signal may get lost.

For example:

var signal = new ManualResetEvent(false);

// Thread 1
signal.WaitOne();

// Thread 2
signal.Set();

This will always work no matter in which the two statements in the different threads are executed. It's also a very clean abstraction and expresses very clearly your intent.

Now have a look at the same example using a monitor:

var signal = new object();

// Thread 1
lock (signal)
{
    Monitor.Wait(signal);
}

// Thread 2
lock (signal)
{
    Monitor.Pulse(signal);
}

Here the signal (Pulse) will get lost if Pulse is executed before Wait.

To fix this problem, you need something like this:

var signal = new object();
var signalSet = false;

// Thread 1
lock (signal)
{
    while (!signalSet)
    {
        Monitor.Wait(signal);
    }
}

// Thread 2
lock (signal)
{
    signalSet = true;
    Monitor.Pulse(signal);
}

This works and is probably even more performant and lightweight, but is less readable. And it's where the headache called concurrency starts.

  • Does this code really work?
  • In every corner case?
  • With more than two threads? (Hint: it doesn't)
  • How do you unit-test it?

A solid, reliable, readable abstraction is often better than raw performance.

Additionally, WaitHandles provide some nice stuff like waiting for a set of handles to be set, etc. Implementing this with monitors makes the headache even worse...


Rule of thumb:

  • Use Monitors (lock) to ensure exclusive access to a shared resource
  • Use WaitHandles (Manual/AutoResetEvent/Semaphore) to send signals between threads
dtb
very good explanation.
J.W.
Thanks for explaining it, that makes perfect sense.
Matt
I love this place... you can easily find and answer like this that will save you hours of researching through books and articles...
Padu Merloti
dtb - Can you please explain the 3rd bullet - Why it breaks with more than two threads?
Miguel Sevilla