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.
- namespace ThreadCodes
- {
-
- public class DeadLock
- {
- private static object lockA = new object();
- private static object lockB = new object();
-
- #region Deadlock Case
-
- private static void CompleteWork1()
- {
- lock (lockA)
- {
- Console.WriteLine("Trying to Acquire lock on lockB");
-
- lock (lockB)
- {
- Console.WriteLine("Critical Section of CompleteWork1");
-
- }
- }
- }
-
- private static void CompleteWork2()
- {
- lock (lockB)
- {
- Console.WriteLine("Trying to Acquire lock on lockA");
-
- lock (lockA)
- {
- Console.WriteLine("Critical Section of CompleteWork2");
-
- }
- }
- }
-
- public static void ExecuteDeadLockCode()
- {
- Thread thread1 = new Thread(CompleteWork1);
- Thread thread2 = new Thread(CompleteWork2);
-
- thread1.Start();
- thread2.Start();
-
- thread1.Join();
- thread2.Join();
-
-
- Console.WriteLine("Processing Completed....");
-
- }
-
- #endregion
- }
- }
Now, execute Deadlock code,
- namespace ThreadCodes
- {
- class Program
- {
- static void Main(string[] args)
- {
- DeadLock.ExecuteDeadLockCode();
- Console.ReadKey();
- }
- }
- }
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.
- namespace ThreadCodes
- {
-
- public class DeadLock
- {
- private static object lockA = new object();
- private static object lockB = new object();
-
- #region Deallock Avoidance
- private static void MyWork1()
- {
- lock (lockA)
- {
- Console.WriteLine("Trying to acquire lock on lockB");
-
-
- if (Monitor.TryEnter(lockB, 5000))
- {
- try
- {
-
- Console.WriteLine("In DoWork1 Critical Section.");
-
- }
- finally
- {
- Monitor.Exit(lockB);
- }
- }
- else
- {
-
- Console.WriteLine("Unable to acquire lock, exiting MyWork1.");
- }
- }
- }
-
- private static void MyWork2()
- {
- lock (lockB)
- {
- Console.WriteLine("Trying to acquire lock on lockA");
- lock (lockA)
- {
- Console.WriteLine("In MyWork2 Critical Section.");
-
- }
- }
- }
-
- public static void ExecuteDeadlockAvoidance()
- {
-
- Thread thread1 = new Thread(MyWork1);
-
-
- Thread thread2 = new Thread(MyWork2);
-
-
- thread1.Start();
- thread2.Start();
-
- thread1.Join();
- thread2.Join();
-
- Console.WriteLine("Done Processing...");
- }
-
- #endregion
-
- }
- }
Now, execute the code.
- namespace ThreadCodes
- {
- class Program
- {
- static void Main(string[] args)
- {
- DeadLock.ExecuteDeadlockAvoidance();
- Console.ReadKey();
- }
- }
- }
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.