Deadlock And Ways To Avoid It

Introduction

In a multi-threaded system, if a resource is locked by one thread but it is required by another thread and vice-versa, then system hangs and this leads to a Deadlock situation. It is a common and very famous problem in multiprocessing system, parallel computing, and distributed systems. It is one of the hardest problems in multi-threaded systems. Thread Synchronization may be the cause of Deadlock situations.
 
The following problems may occur due to Thread Synchronization.
  • Deadlock
  • Starvation
  • Priority Inversion
  • Busy Waiting
You can see one very common and real world example of deadlock prevention and avoidance:
 
There might be the situation of deadlock in a four crossing road. So, a traffic light system is used to prevent the deadlock on the road and the traffic police works to avoid the deadlock.
 
Code Example of Deadlock

Below is an example of deadlock.
  1. namespace ThreadCodes  
  2. {  
  3.   
  4.     public class DeadLock  
  5.     {  
  6.         private static object lockA = new object();  
  7.         private static object lockB = new object();  
  8.  
  9.         #region Deadlock Case  
  10.   
  11.         private static void CompleteWork1()  
  12.         {  
  13.             lock (lockA)  
  14.             {  
  15.                 Console.WriteLine("Trying to Acquire lock on lockB");  
  16.   
  17.                 lock (lockB)  
  18.                 {  
  19.                     Console.WriteLine("Critical Section of CompleteWork1");  
  20.                     //Access some shared critical section.  
  21.                 }  
  22.             }  
  23.         }  
  24.   
  25.         private static void CompleteWork2()  
  26.         {  
  27.             lock (lockB)  
  28.             {  
  29.                 Console.WriteLine("Trying to Acquire lock on lockA");  
  30.   
  31.                 lock (lockA)  
  32.                 {  
  33.                     Console.WriteLine("Critical Section of CompleteWork2");  
  34.                     //Access some shared critical section.  
  35.                 }  
  36.             }  
  37.         }  
  38.   
  39.         public static void ExecuteDeadLockCode()  
  40.         {  
  41.             Thread thread1 = new Thread(CompleteWork1);  
  42.             Thread thread2 = new Thread(CompleteWork2);  
  43.   
  44.             thread1.Start();  
  45.             thread2.Start();  
  46.   
  47.             thread1.Join();  
  48.             thread2.Join();  
  49.   
  50.             //Below code section will never execute due to deadlock.  
  51.             Console.WriteLine("Processing Completed....");  
  52.   
  53.         }  
  54.  
  55.         #endregion  
  56.  }  
  57. }  
Now, execute Deadlock code,
  1. namespace ThreadCodes  
  2. {  
  3.     class Program  
  4.     {  
  5.         static void Main(string[] args)  
  6.         {  
  7.             DeadLock.ExecuteDeadLockCode();  
  8.             Console.ReadKey();  
  9.         }  
  10.     }  
  11. }  
Deadlock Avoidance 
 
Here, we will use Monitor to avoid the deadlock. It supports Timeout option. If one thread is holding the resource for a long time while the other thread is waiting, Monitor will give  a certain time limit and force the lock to release it. Then, the other thread will enter.

Below is the code example.
  1. namespace ThreadCodes  
  2. {  
  3.   
  4.     public class DeadLock  
  5.     {  
  6.         private static object lockA = new object();  
  7.         private static object lockB = new object();  
  8.  
  9.         #region Deallock Avoidance  
  10.         private static void MyWork1()  
  11.         {  
  12.             lock (lockA)  
  13.             {  
  14.                 Console.WriteLine("Trying to acquire lock on lockB");  
  15.   
  16.                 // This will try to acquire lock for 5 seconds.  
  17.                 if (Monitor.TryEnter(lockB, 5000))  
  18.                 {  
  19.                     try  
  20.                     {  
  21.                         // This block will never be executed.  
  22.                         Console.WriteLine("In DoWork1 Critical Section.");  
  23.                         // Access some shared resource here.  
  24.                     }  
  25.                     finally  
  26.                     {  
  27.                         Monitor.Exit(lockB);  
  28.                     }  
  29.                 }  
  30.                 else  
  31.                 {  
  32.                     // Print lock not able to acquire message.  
  33.                     Console.WriteLine("Unable to acquire lock, exiting MyWork1.");  
  34.                 }  
  35.             }  
  36.         }  
  37.   
  38.         private static void MyWork2()  
  39.         {  
  40.             lock (lockB)  
  41.             {  
  42.                 Console.WriteLine("Trying to acquire lock on lockA");  
  43.                 lock (lockA)  
  44.                 {  
  45.                     Console.WriteLine("In MyWork2 Critical Section.");  
  46.                     // Access some shared resource here.  
  47.                 }  
  48.             }  
  49.         }  
  50.   
  51.         public static void ExecuteDeadlockAvoidance()  
  52.         {  
  53.             // Initialize thread with address of DoWork1  
  54.             Thread thread1 = new Thread(MyWork1);  
  55.   
  56.             // Initilaize thread with address of DoWork2  
  57.             Thread thread2 = new Thread(MyWork2);  
  58.   
  59.             // Start the Threads.  
  60.             thread1.Start();  
  61.             thread2.Start();  
  62.   
  63.             thread1.Join();  
  64.             thread2.Join();  
  65.   
  66.             Console.WriteLine("Done Processing...");  
  67.         }  
  68.  
  69.         #endregion  
  70.   
  71.     }  
  72. }  
Now, execute the code.
  1. namespace ThreadCodes  
  2. {  
  3.     class Program  
  4.     {  
  5.         static void Main(string[] args)  
  6.         {  
  7.             DeadLock.ExecuteDeadlockAvoidance();  
  8.             Console.ReadKey();  
  9.         }  
  10.     }  
  11. }  
So, the above code is the best way to avoid deadlock.
 
Conclusion 

Using Thread Synchronization with Lock is very dangerous. It should be handled very carefully. Monitor is the best way to avoid deadlock and provide thread synchronization.