Search code examples
c#multithreadingthread-synchronization

Do I need a lock if I have EventWaitHandle


Studying multi-threading from albahari atricle. Do I need a lock _locker in the sample below? I suppose no, since _message is protected by EventWaitHandle. Am I right?

class TwoWaySignaling
{
  static EventWaitHandle _ready = new AutoResetEvent (false);
  static EventWaitHandle _go = new AutoResetEvent (false);
  static readonly object _locker = new object();
  static string _message;

  static void Main()
  {
    new Thread (Work).Start();

    _ready.WaitOne();                  // First wait until worker is ready
    lock (_locker) _message = "ooo";
    _go.Set();                         // Tell worker to go

    _ready.WaitOne();
    lock (_locker) _message = "ahhh";  // Give the worker another message
    _go.Set();
    _ready.WaitOne();
    lock (_locker) _message = null;    // Signal the worker to exit
    _go.Set();
  }

  static void Work()
  {
    while (true)
    {
      _ready.Set();                          // Indicate that we're ready
      _go.WaitOne();                         // Wait to be kicked off...
      lock (_locker)
      {
        if (_message == null) return;        // Gracefully exit
        Console.WriteLine (_message);
      }
    }
  }
}

Solution

  • You are correct.

    These kind of problems cannot be simply tested with trial and error. They are best analyzed with logical thinking:

    • Which thread runs which code?
    • What could happen in the other thread right now?
    • What happens if the other thread gets more CPU time? etc.

    The main thread will only execute the code in Main() and the worker thread will only execute the code in Work(). So that's simple enough.

    If you look at how the critical resource is accessed you will note that access to _message in Main() is always between

    _ready.WaitOne();
    

    and

    _go.Set();
    

    whereas access to _message in Work() is always between

    _go.WaitOne();
    

    and

    _ready.Set();
    

    Therefore, one of the threads will always wait for the other before accessing _message and the lock is not needed.