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.

C# Lock

C# Lock keyword ensures that 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

namespace Monitor_Lock  
{  
    class Program  
    {  
        static readonly object _object = new object();  
  
        static void TestLock()  
        {  
              
            lock (_object)  
            {  
                Thread.Sleep(100);  
                Console.WriteLine(Environment.TickCount);  
            }  
        }  
  
        static void Main(string[] args)      
        {  
            for (int i = 0; i < 10; i++)  
            {  
                ThreadStart start = new ThreadStart(TestLock);  
                new Thread(start).Start();  
            }  
  
            Console.ReadLine();  
        }  
    }  
}

Output

C# Lock

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

C# 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.

try  
{  
    int x = 1;  
      
   Monitor.Enter(x);  
    try  
    {  
        // Code that needs to be protected by the monitor.  
    }  
    finally  
    {  
          
       Monitor.Exit(x);  
    }  
}  
catch (SynchronizationLockException SyncEx)  
{  
    Console.WriteLine("A SynchronizationLockException occurred. Message:");  
    Console.WriteLine(SyncEx.Message);  
}

Sample Code

namespace Monitor_Lock  
{  
    class Program  
    {  
        static readonly object _object = new object();  
  
        public static void PrintNumbers()  
        {  
            Monitor.Enter(_object);  
            try  
            {  
                for (int i = 0; i < 5; i++)  
                {  
                    Thread.Sleep(100);  
                    Console.Write(i + ",");  
                }  
                Console.WriteLine();  
            }  
            finally  
            {  
                Monitor.Exit(_object);  
            }  
        }  
  
        static void TestLock()  
        {  
              
            lock (_object)  
            {  
                Thread.Sleep(100);  
                Console.WriteLine(Environment.TickCount);  
            }  
        }  
  
        static void Main(string[] args)      
        {  
  
              
            Thread[] Threads = new Thread[3];  
            for (int i = 0; i < 3; i++)  
            {  
                Threads[i] = new Thread(new ThreadStart(PrintNumbers));  
                Threads[i].Name = "Child " + i;  
            }  
            foreach (Thread t in Threads)  
                t.Start();  
  
            Console.ReadLine();  
        }  
    }  
}

Output

C# Monitor

In C# 4.0, overloaded function for Monitor.Enter(_object,ref _lockTaken) acquires an exclusive lock and the specified object, and automatically sets a value that indicates whether the lock was taken.

C# Monitor

class Program  
{  
     static readonly object _object = new object();  
  
     public static void PrintNumbers()  
     {  
         Boolean _lockTaken = false;  
  
         Monitor.Enter(_object,ref _lockTaken);  
         try  
         {  
            for (int i = 0; i < 5; i++)  
            {  
               Thread.Sleep(100);  
               Console.Write(i + ",");  
            }  
            Console.WriteLine();  
         }  
         finally  
         {  
            if (_lockTaken)  
            {  
               Monitor.Exit(_object);  
            }  
         }  
     }
}

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 when we want more control over synchronization with multiple threads running for a specific section of code.


Similar Articles