About
In this code snippet, we will take a look at thread deadlocks in C# and how to resolve them.
If you see this post I made about thread resource locking you will see why a thread would need to lock a resource to prevent other threads from using it. But when you start locking down resources you could potentially run into a deadlock.
A thread deadlock occurs in such a situation:
-
- Thread 1 acquires a lock on resource A.
- Thread 2 acquires a lock on resource B.
- Thread 1 needs resource B to complete its execution.
- Thread 2 needs resource A to complete its execution.
- Both threads are now waiting on each other to finish.
To resolve this deadlock we need to change our code in such a way:
- Thread 1 acquires a lock on resource A.
- Thread 2 tries to acquire a lock on resource A but fails because it’s already locked.
- Thread 1 acquires a lock on resource B and completes its execution. After that, the lock on A and B is released.
- Thread 2 acquires a lock on A.
- Thread 2 acquires a lock on B and completes its execution.
Basically what you have to do is make sure that you are locking resources in the same order in all the threads. This way when the first common resource is locked by one thread no other thread can continue executing and locking on any other common resource required by the first thread.
Let’s have a look at the code below to see how to resolve a deadlock.
Code:
using System; using System.Threading; namespace ThreadDeadlock { class Program { static void Main(string[] args) { MyClass MC = new MyClass(); //Make threads. Thread th1 = new Thread(() => MC.transferFromAToB(100)); Thread th2 = new Thread(() => MC.transferFromBToA(100)); //Start threads. th1.Start(); th2.Start(); Console.ReadLine(); } } class MyClass { private int a; private int b; //lock objects. private Object aLock = new Object(); private Object bLock = new Object(); public MyClass() { a = 1000; b = 1000; } #region Deadlock Code public void transferFromAToB(int amount) { lock (aLock) { //Simulate computing time to get the value; Thread.Sleep(100); a = a - amount; lock (bLock) { //Simulate computing time to get the value; Thread.Sleep(100); b = b + amount; } } Console.WriteLine(amount + " was transfered from A to B."); } public void transferFromBToA(int amount) { lock (bLock) { //Simulate computing time to get the value; Thread.Sleep(100); b = b - amount; lock (aLock) { //Simulate computing time to get the value; Thread.Sleep(100); a = a + amount; } } Console.WriteLine(amount + " was transfered from B to A."); } #endregion #region Resolved deadlock Code /* public void transferFromAToB(int amount) { lock (aLock) { //Simulate computing time to get the value; Thread.Sleep(100); a = a - amount; lock (bLock) { //Simulate computing time to get the value; Thread.Sleep(100); b = b + amount; } } Console.WriteLine(amount + " was transfered from A to B."); } public void transferFromBToA(int amount) { lock (aLock) { //Simulate computing time to get the value; Thread.Sleep(100); a = a + amount; lock (bLock) { //Simulate computing time to get the value; Thread.Sleep(100); b = b - amount; } } Console.WriteLine(amount + " was transfered from B to A."); } */ #endregion } }