Threading Simplified: Part 10 (Monitor)

Before starting, you can refer to  previous posts on Threading:

I am again here to continue the discussion around Threading. Today, we will discuss the uses of Monitor and related concepts.

Let’s start by putting some questions to understand the concepts.

What is Monitor in Multithreading?

Monitor is used to synchronize access to any code block by allowing only one thread at a time.

Is Monitor is similar to using lock keyword?

At high level yes and also for most cases monitor and lock works the same way. However, monitor provides more control when dealing with various locking situations

Let’s understand this by simple example.

  1. class TestMonitor  
  2. {  
  3.     int num1 = 0;  
  4.     int num2 = 0;  
  5.     Random rnd = new Random();  
  6.     private static objectmyLock = newo bject();  
  7.     public void DoDivide()   
  8.     {  
  9.         Monitor.Enter(myLock); //// This ensures that only one thread can enter.   
  10.         {  
  11.             try   
  12.             {  
  13.                 for (inti = 0; i < 5; i++)   
  14.                 {  
  15.                     num1 = rnd.Next(1, 5);  
  16.                     num2 = rnd.Next(1, 5);  
  17.                     Console.WriteLine("Division of {0} and {1} is: {2}", num1, num2, num1 / num2);  
  18.                     num1 = 0;  
  19.                     num2 = 0;  
  20.                 }  
  21.             } finally   
  22.             {  
  23.                 Monitor.Exit(myLock); ////This releases the lock.  
  24.             }  
  25.         }  
  26.     }  
  27. }  
So, what’s the difference we have here when comparing to lock keyword?
 
Well, the answer is that here we need to explicitly handle monitor exit operations.

So can we say that lock keyword is equal to using Monitor.Enter and Monitor.Exit?

In most cases yes, but not when due to some critical issues such as OutOfMemoryException occurrs or thread is aborted while execution was between Monitor, Enter, and try block. Due to that the final block will not be executed to release the lock. On the contrary, lock keyword automatically releases the lock even in the case mentioned above.

So how can we write monitor code that is secure in all cases?

To handle a situation when lock cannot be released, there is an overload provided in the Monitor.Enter method. Let’s implement this by adding a new bool variable and making a small change in the DoDivide method referred to in the previous example.
  1. class TestMonitor  
  2. {  
  3.     ////Code similar to previous example  
  4.     public void DoDivide()   
  5.     {  
  6.         bool isLockTaken = false;  
  7.         try   
  8.         {  
  9.             Monitor.Enter(myLock, refisLockTaken);  
  10.           {  
  11.                 for (int i = 0; i < 5; i++)   
  12.                 {  
  13.                     num1 = rnd.Next(1, 5);  
  14.                     num2 = rnd.Next(1, 5);  
  15.                     Console.WriteLine("Division of {0} and {1} is: {2}", num1, num2, num1 / num2);  
  16.                     num1 = 0;  
  17.                     num2 = 0;  
  18.                 }  
  19.             }  
  20.         } finally {  
  21.             if (isLockTaken)  
  22.             {  
  23.                 Monitor.Exit(myLock);  
  24.             }  
  25.         }  
  26.     }  
  27. }  
So now this monitor implementation is equal in real terms to the lock keyword example, referred in the following article.

 

Now the method is ready, let’s create some threads and see the output.

  1. class Program  
  2. {  
  3.     static void Main(string[] args)  
  4.     {  
  5.         Console.Title = "Monitor Demo";  
  6.         TestMonitor testThreadSafe = new TestMonitor();  
  7.         Thread t1 = new Thread(testThreadSafe.DoDivide);  
  8.         Thread t2 = new Thread(testThreadSafe.DoDivide);  
  9.         t1.Start();  
  10.         t2.Start();  
  11.     }  
  12. }  
Output:

output

Monitor also provides TryEnter method that allows timeout to be specified when obtaining lock. It returns true if lock is obtained or false if lock could not be obtained in the given period.

Once lock is obtained, we can use the Monitor.Wait, Monitor.Pulse, and Monitor.PulseAll methods to further control the synchronization. We will talk about these methods in some future article however in a nutshell, these methods are associated with an object on demand and are unbound which makes them be called directly from any context.

Hope you liked the article. I look forward to your comments/suggestions.
 
Read more articles on Threading:

 

X

Build smarter apps with Machine Learning, Bots, Cognitive Services - Start free.

Start Learning Now