Introduction to Multithreading Part 2

We have already learned the basic key concepts of multithreading. If you haven't then please refer to my previous article on the basics of multithreading.

This article explains the various techniques of thread synchronization in multithreaded applications. A multithreaded application contains multiple threads working concurrently. Whenever we have multiple threads that share some data, there may develop some issues related to concurrent access of variables and objects accessible by two or more threads at the same time. To overcome such issues, it is necessary to ensure that only one thread has a chance to acquire a lock on a shared object at a time. Until the time that thread is locking a specific resource, all other threads waits for that resource until the lock is released.

This article we will explain the following four concepts of thread synchronization:

  1. Lock
  2. Monitor
  3. Mutex
  4. Semaphore

Exclusive locking ensures that only one thread can enter a specific section of code at a time. Both lock and mutex are exclusive locking constructs. Among these two lock construct is fast.

Lock: Let's consider a simple example of dividing two numbers.

  1. class ThreadSync  
  2. {  
  3.     int a = 4, b = 2;  
  4.   
  5.     public void Divide()  
  6.     {  
  7.         if (b != 0)  
  8.             Console.WriteLine(a / b);  
  9.         b = 0;  
  10.     }  
  11. }  
In the preceding code snippet, if two threads call the method Divide() then there is a possibility that one thread will throw a divide by zero exception. To overcome this problem, one thread will lock this section of code and no other thread can enter this locked section until the lock is released. All other threads are in a blocked state.

A thread-safe version of this code is as follows:
  1. class ThreadSync  
  2. {  
  3.     public static readonly object _lock = new object();  
  4.     int a = 4, b = 2;  
  5.   
  6.     public void Divide()  
  7.     {  
  8.         lock (_lock)  
  9.         {  
  10.             if (b != 0)  
  11.                 Console.WriteLine(a / b);  
  12.             b = 0;  
  13.         }  
  14.     }  
  15. }  
Here the argument provided to the lock keyword must be an object based on a reference type. This is used to define the scope of the lock.

Monitor: Similar to locks, a monitor also prevents blocks of code from simultaneous execution by multiple threads. You can say that a lock statement is a shortcut to call Monitor class methods. The two main methods of the Monitor class used are Monitor.Enter and Monitor.Exit with a try/finally block. The following is the code using Monitor:
  1. class ThreadSync  
  2. {  
  3.     public static readonly object _lock = new object();  
  4.     int a = 4, b = 2;  
  5.   
  6.     public void Divide()  
  7.     {  
  8.         Monitor.Enter(_lock);  
  9.         try  
  10.         {  
  11.             if (b != 0)  
  12.                 Console.WriteLine(a / b);  
  13.             b = 0;  
  14.         }  
  15.         finally  
  16.         {  
  17.             Monitor.Exit(_lock);  
  18.         }  
  19.     }  
  20. }  
The Monitor.Enter() method of the System.Threading.Monitor class is the recipient of the thread token. The entire code that needs to be locked should be written inside a try block. The finally block will ensure that the thread token is released using the Monitor.Exit() method.

Mutex: A Mutex is just like the lock construct but it can work across multiple processes. The mutex can be computer-wide as well as application-wide. A Mutex is slower than the lock construct. The two methods for acquiring and releasing a lock of the Mutex class are WaitOne and ReleaseMutex method. Otherwise, closing or disposing the mutex automatically releases the lock. Mutex is generally used for interprocess synchronization, hence it is called a named mutex because it is to be used in another application and cannot be shared by means of a global or static variable.

A Mutex class is basically a wrapper around a WIN32 Mutex.
  1. class ThreadSyn  
  2. {  
  3.     private Mutex _mut = new Mutex();  
  4.   
  5.     public void DoSomething()  
  6.     {  
  7.         try  
  8.         {  
  9.             _mut.WaitOne();  
  10.             //perform operation  
  11.         }  
  12.         finally  
  13.         {  
  14.             _mut.ReleaseMutex();  
  15.         }  
  16.     }  
  17. }  
Semaphore: The Semaphore class allows a specified number of threads to access a resource. All other threads are blocked until a thread releases the lock. A semaphore with a capacity of one thread is similar to a lock or Monitor construct with the only exception that the semaphore has no owner.

Any thread can call the Release method of the Semaphore class, whereas with a Mutex and a lock only the thread that obtained the lock can release it.
  1. class ThreadSyn  
  2. {  
  3.     private Semaphore _sem = new Semaphore(0, 3);  
  4.   
  5.     public void DoSomething()  
  6.     {  
  7.         try  
  8.         {  
  9.             _sem.WaitOne();  
  10.             //perform action  
  11.         }  
  12.         finally  
  13.         {  
  14.             _sem.Release();  
  15.   
  16.         }  
  17.     }  
  18. }  
Here we limit the count of threads to enter this specific section of code to 3. So the code can handle 3 threads without making the application unresponsive. In simple terms, only three threads at a time can access this section of code or say resource. 

Next Recommended Readings