C# Monitor

Contents

What is Monitor In C#?

Monitor class is one of the wait based synchronization primitive that provides gated access to the resource. It gates or throttles the access to the shared resource.

So,

Monitor assure that thread access the shared resource one thread at a time

Here is the code to use Monitor for shared resources to avoid the race condition

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading;
 6 using System.Threading.Tasks;
 7 
 8 namespace _05_Monitor_Class_Usage
 9 {
10     class Program
11     {
12         private static int sum;
13         private static object _lock = new object();
14 
15         static void Main(string[] args)
16         {
17 
18             //create thread t1 using anonymous method
19             Thread t1 = new Thread(() => {
20                 for (int i = 0; i < 10000000; i++)
21                 {
22                     //acquire lock ownership
23                     Monitor.Enter(_lock);
24 
25                     //increment sum value
26                     sum++;
27 
28                     //release lock ownership
29                     Monitor.Exit(_lock);
30                 }
31             });
32 
33             //create thread t2 using anonymous method
34             Thread t2 = new Thread(() => {
35                 for (int i = 0; i < 10000000; i++)
36                 {
37                     //acquire lock ownership
38                     Monitor.Enter(_lock);
39 
40                     //increment sum value
41                     sum++;
42 
43                     //release lock ownership
44                     Monitor.Exit(_lock);
45                 }
46             });
47 
48 
49             //start thread t1 and t2
50             t1.Start();
51             t2.Start();
52 
53             //wait for thread t1 and t2 to finish their execution
54             t1.Join();
55             t2.Join();
56 
57             //write final sum on screen
58             Console.WriteLine("sum: " + sum);
59 
60             Console.WriteLine("Press enter to terminate!");
61             Console.ReadLine();
62         }
63     }
64 }

Here, sum++ is considered as the critical section, as this operation should be done in a thread-safe manner we use the monitor to carry out this operation as one thread at a time.

As you saw Monitor use Enter and Exit method which accepts an object to associate with lock primitive

Why?

locks are created by CLR only when you use monitor API to get acquisition of a lock (for performance reasons), so basically they are maintained as a table of the lock by CLR.

So,

When you pass this object in monitor method this object stores the index of the lock object created by CLR in their header as information to use this lock for gated access to the resource.

In short, this object is not the actual lock but stores reference to the lock object use by monitor class to access the resource in wait based manner.

Basically, you can use any type of object to associate with a lock. However, the recommended method is to use private objects and always avoid string as lock objects as the issue they cause due to their implementation method in CLR

Monitor Class Usage In C#

  • So Monitor has the same wait based technique usage. When you call the Monitor.Enter method you get the ownership of the lock, then you perform your thread-safe operation and then release the lock using Monitor.Exit.

  • Always remember this is the programmer understanding to use wait based technique thoughtfully at the required places as there are no physical restrictions on how you access the resource or implement the model. It’s just you deciding how to implement the flow of multithreading program and shared resources.

Exception Aware Monitor Usage

Now,

Consider the same example code above and there are two threads trying to acquire the lock X and Y now thread X got the ownership and Y got blocked until X releases the ownership.

However,

Before releasing the lock thread X threw some runtime exception error hence it will exit the code before releasing the lock, as a result, thread Y will get blocked forever.

We want to throw the exception but also want to release the lock.

So,

To overcome this problem we have to use proper try-finally construct to manage the exception (not handle it). Let’s see the code how to do it,

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading;
 6 using System.Threading.Tasks;
 7 
 8 namespace _06_Exception_Aware_Monitor
 9 {
10     class Program
11     {
12         private static int sum;
13         private static object _lock = new object();
14 
15         static void Main(string[] args)
16         {
17 
18             //create thread t1 using anonymous method
19             Thread t1 = new Thread(() => {
20                 for (int i = 0; i < 10000000; i++)
21                 {
22                     //acquire lock ownership
23                     Monitor.Enter(_lock);
24                     try
25                     {
26                         //increment sum value
27                         sum++;
28                     }
29                     finally
30                     {
31                         //release lock ownership
32                         Monitor.Exit(_lock);
33                     }                    
34                 }
35             });
36 
37             //create thread t2 using anonymous method
38             Thread t2 = new Thread(() => {
39                 for (int i = 0; i < 10000000; i++)
40                 {
41                     //acquire lock ownership
42                     Monitor.Enter(_lock);
43                     try
44                     {
45                         //increment sum value
46                         sum++;
47                     }
48                     finally
49                     {
50                         //release lock ownership
51                         Monitor.Exit(_lock);
52                     }
53                 }
54             });
55 
56 
57             //start thread t1 and t2
58             t1.Start();
59             t2.Start();
60 
61             //wait for thread t1 and t2 to finish their execution
62             t1.Join();
63             t2.Join();
64 
65             //write final sum on screen
66             Console.WriteLine("sum: " + sum);
67 
68             Console.WriteLine("Press enter to terminate!");
69             Console.ReadLine();
70         }
71     }
72 }

C# Lock Keyword

Some high level languages have syntactic sugar which reduces the amount of code that must be written in some common situation like above.

C# has this lock syntax for the same code we wrote above. Here is the code

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading;
 6 using System.Threading.Tasks;
 7 
 8 namespace _07_Lock_Keyword
 9 {
10     class Program
11     {
12         private static int sum;
13         private static object _lock = new object();
14 
15         static void Main(string[] args)
16         {
17 
18             //create thread t1 using anonymous method
19             Thread t1 = new Thread(() => {
20                 for (int i = 0; i < 10000000; i++)
21                 {
22                     lock (_lock)
23                     {
24                         //increment sum value
25                         sum++;
26                     }
27                 }
28             });
29 
30             //create thread t2 using anonymous method
31             Thread t2 = new Thread(() => {
32                 for (int i = 0; i < 10000000; i++)
33                 {
34                     lock(_lock)
35                     {
36                         //increment sum value
37                         sum++;
38                     }
39                 }
40             });
41 
42 
43             //start thread t1 and t2
44             t1.Start();
45             t2.Start();
46 
47             //wait for thread t1 and t2 to finish their execution
48             t1.Join();
49             t2.Join();
50 
51             //write final sum on screen
52             Console.WriteLine("sum: " + sum);
53 
54             Console.WriteLine("Press enter to terminate!");
55             Console.ReadLine();
56         }
57     }
58 }

so we simply use the lock keyword syntax and write critical section code in its body and compiler will generate the Exception Aware Monitor code for us. Sweet!

In the next post C# Task, we will learn more about Tasks in C#. Tasks provide a sophisticated way to handle async or parallel operation. Task object typically executes asynchronously on a thread pool thread.

C# Monitor
Share this

Subscribe to Code with Shadman