Monitor and Lock in C#

Introduction

When working with a multithreading application it is very important for developers to handle multiple threads for a critical section of code.

Monitor and lock is the way to provide thread safety in a multithreaded application in C#. Both provide a mechanism to ensure that only one thread is executing code at the same time to avoid any functional breaking of code.

Lock

Lock is the keyword in C# that will ensure one thread is executing a piece of code at one time. The lock keyword ensures that one thread does not enter a critical section of code while another thread is in that critical section.

Lock is a keyword shortcut for acquiring a lock for the piece of code for only one thread.

Sample Code

  1. namespace Monitor_Lock  
  2. {  
  3.     class Program  
  4.     {  
  5.         static readonly object _object = new object();  
  6.   
  7.         static void TestLock()  
  8.         {  
  9.               
  10.             lock (_object)  
  11.             {  
  12.                 Thread.Sleep(100);  
  13.                 Console.WriteLine(Environment.TickCount);  
  14.             }  
  15.         }  
  16.   
  17.         static void Main(string[] args)      
  18.         {  
  19.             for (int i = 0; i < 10; i++)  
  20.             {  
  21.                 ThreadStart start = new ThreadStart(TestLock);  
  22.                 new Thread(start).Start();  
  23.             }  
  24.   
  25.             Console.ReadLine();  
  26.         }  
  27.     }  
  28. }  
Output



Here we see a static method "TestLock" that uses the lock statement on an object. When the method TestLock is called many times on new threads, each invocation of the method accesses the threading primitives implemented by the lock.

The Main method creates ten new threads and then calls Start on each one. The method TestLock is invoked ten times, but the tick count shows the protected method region is executed sequentially, about 100 milliseconds apart.

If another thread tries to enter a locked code, it will wait, block, until the object is released.

The lock keyword marks a statement block as a critical section by obtaining the mutual-exclusion lock for a given object, executing a statement and then releasing the lock

Monitor

Monitor provides a mechanism that synchronizes access to objects. It can be done by acquiring a significant lock so that only one thread can enter in a given piece of code at one time. Monitor is no different from lock but the monitor class provides more control over the synchronization of various threads trying to access the same lock of code.

Using a monitor it can be ensured that no other thread is allowed to access a section of application code being executed by the lock owner, unless the other thread is executing the code using a different locked object.

The Monitor class has the following methods for the synchronize access to a region of code by taking and releasing a lock:

 

  • Monitor.Enter 
  • Monitor.TryEnter
  • Monitor.Exit.
Monitor locks objects (that is, reference types), not value types. While you can pass a value type to Enter and Exit, it is boxed separately for each call.

Wait releases the lock if it is held and waits to be notified. When Wait is notified, it returns and obtains the lock again. Both a Pulse and PulseAll signal for the next thread in the wait queue to proceed.

The following is the syntax for using a monitor.
  1. try  
  2. {  
  3.     int x = 1;  
  4.       
  5.    Monitor.Enter(x);  
  6.     try  
  7.     {  
  8.         // Code that needs to be protected by the monitor.  
  9.     }  
  10.     finally  
  11.     {  
  12.           
  13.        Monitor.Exit(x);  
  14.     }  
  15. }  
  16. catch (SynchronizationLockException SyncEx)  
  17. {  
  18.     Console.WriteLine("A SynchronizationLockException occurred. Message:");  
  19.     Console.WriteLine(SyncEx.Message);  
  20. }  
Sample Code
  1. namespace Monitor_Lock  
  2. {  
  3.     class Program  
  4.     {  
  5.         static readonly object _object = new object();  
  6.   
  7.         public static void PrintNumbers()  
  8.         {  
  9.             Monitor.Enter(_object);  
  10.             try  
  11.             {  
  12.                 for (int i = 0; i < 5; i++)  
  13.                 {  
  14.                     Thread.Sleep(100);  
  15.                     Console.Write(i + ",");  
  16.                 }  
  17.                 Console.WriteLine();  
  18.             }  
  19.             finally  
  20.             {  
  21.                 Monitor.Exit(_object);  
  22.             }  
  23.         }  
  24.   
  25.         static void TestLock()  
  26.         {  
  27.               
  28.             lock (_object)  
  29.             {  
  30.                 Thread.Sleep(100);  
  31.                 Console.WriteLine(Environment.TickCount);  
  32.             }  
  33.         }  
  34.   
  35.         static void Main(string[] args)      
  36.         {  
  37.   
  38.               
  39.             Thread[] Threads = new Thread[3];  
  40.             for (int i = 0; i < 3; i++)  
  41.             {  
  42.                 Threads[i] = new Thread(new ThreadStart(PrintNumbers));  
  43.                 Threads[i].Name = "Child " + i;  
  44.             }  
  45.             foreach (Thread t in Threads)  
  46.                 t.Start();  
  47.   
  48.             Console.ReadLine();  
  49.         }  
  50.     }  
  51. }  
Output



In C# 4.0 , overloaded function for Monitor.Enter(_object,ref _lockTaken)

That acquires an exclusive lock and the specified object and automatically sets a value that indicates whether the lock was taken.


  1. class Program  
  2. {  
  3.      static readonly object _object = new object();  
  4.   
  5.      public static void PrintNumbers()  
  6.      {  
  7.          Boolean _lockTaken = false;  
  8.   
  9.          Monitor.Enter(_object,ref _lockTaken);  
  10.          try  
  11.          {  
  12.             for (int i = 0; i < 5; i++)  
  13.             {  
  14.                Thread.Sleep(100);  
  15.                Console.Write(i + ",");  
  16.             }  
  17.             Console.WriteLine();  
  18.          }  
  19.          finally  
  20.          {  
  21.             if (_lockTaken)  
  22.             {  
  23.                Monitor.Exit(_object);  
  24.             }  
  25.          }  
         }
  26. }  
Important Sticky

The Monitor class is a static class and its instance cannot be created.

The Monitor class object uses the Monitor.Enter, Monitor.TryEnter, and Monitor.Exit methods. Once you have a lock on a code region, you can use the Monitor.Wait, Monitor.Pulse, and Monitor.PulseAll methods.

It is associated with an object on demand.

It is unbound, which means it can be called directly from any context.

Conclusion

Lock and monitor are basically used for the same purpose in multithreading, the difference is that only when we want more control over synchronization with multiple threads running for a specific section of code.

Up Next
    Ebook Download
    View all
    Learn
    View all