Deadlock in C#

Deadlock in C# with Example

In this article, I am going to discuss Deadlock in C# with an example. Please read our previous article where we discussed Semaphore in C# with some examples. As part of this article, we will discuss what a deadlock is and then we will discuss why and how a deadlock can occur in a multithreaded application. And in the next article, I will show you how to solve the deadlock problems in C#.

What is a Deadlock in C#?

In simple words, we can define a deadlock in C# is a situation where two or more threads are unmoving or frozen in their execution because they are waiting for each other to finish.

For example, let’s say we have two threads Thread1 and Thread2 and at the same time let say we have two resources Resource1 and Resource2. The Thread1 locked the Resource1 and trying to acquire a lock on Respurce2. At the same time, Thread2 acquired a lock on Resource2 and trying to acquire a lock on Resource1.

Deadlock in C# in Multithread application

As you can see in the above image, Thread1 is waiting to acquire a lock on Resource2 which is held by Thread2. Thread2 also can’t finish his work and release the lock on Resource2 because it is waiting to acquire a lock on Resource1 which is locked by Thread1, and hence a Deadlock situation occurred.

Example:

Let us understand Deadlock in C# with an example.

Create a class file with the name Account.cs and then copy and paste the following code in it.

namespace DeadLockDemo
{
    public class Account
    {
        public int ID { get; }
        private double Balance;

        public Account(int id, double balance)
        {
            ID = id;
            Balance = balance;
        }
        
        public void WithdrawMoney(double amount)
        {
            Balance -= amount;
        }

        public void DepositMoney(double amount)
        {
            Balance += amount;
        }
    }
}

The above Account class is very straight forward. We created the class with properties i.e. ID and Balance. Through the constructor of this class, we are initializing these properties. So, at the time of Account class instance creation, we need to pass the ID and Balance value. Here we have also created two methods. The WithdrawMoney method is used for withdrawing the amount while the DepositMoney method is used for adding the amount.

AccountManager.cs:

Create a class file with the name AccountManager.cs and then copy and paste the following code in it.

using System;
using System.Threading;

namespace DeadLockDemo
{
    public class AccountManager
    {
       private Account FromAccount;
       private Account ToAccount;
       private double TransferAmount;

        public AccountManager(Account AccountFrom, Account AccountTo, double AmountTransfer)
        {
            FromAccount = AccountFrom;
            ToAccount = AccountTo;
            TransferAmount = AmountTransfer;
        }

        public void FundTransfer()
        {
            Console.WriteLine($"{Thread.CurrentThread.Name} trying to acquire lock on {FromAccount.ID}");
            lock (FromAccount)
            {
                Console.WriteLine($"{Thread.CurrentThread.Name} acquired lock on {FromAccount.ID}");
                Console.WriteLine($"{Thread.CurrentThread.Name} Doing Some work");
                Thread.Sleep(1000);
                Console.WriteLine($"{Thread.CurrentThread.Name} trying to acquire lock on {ToAccount.ID}");

                lock (ToAccount)
                {
                    FromAccount.WithdrawMoney(TransferAmount);
                    ToAccount.DepositMoney(TransferAmount);
                }
            }
        }
    }
}

In the above code, we created two Account type variables to hold the FromAccount and ToAccount details i.e. the Account from where the amount is going to deducted and the account to whom the amount is created. We also created another double type variable i.e. TransferAmount to hold the amount which is going to be deducted from the FromAccount and credited to the ToAccount. Through the constructor of this class, we are initializing the class variables.

We also created the FundTransfer method which is going to perform the required task. As you can see, it first acquires a lock on From Account and then doing some work. After 1 second it backs and trying to acquire a lock on To Account.

Modifying the Main Method:

Now modify the Main method of Program class as shown below. Here, for accountManager1, Account1001 is the FromAccount and Account1002 is the ToAccount. Similarly, for accountManager2, Account1002 is the FromAccount and Account1001 is the ToAccount

using System;
using System.Threading;

namespace DeadLockDemo
{
    class Program
    {
        public static void Main()
        {
            Console.WriteLine("Main Thread Started");
            Account Account1001 = new Account(1001, 5000);
            Account Account1002 = new Account(1002, 3000);

            AccountManager accountManager1 = new AccountManager(Account1001, Account1002, 5000);
            Thread thread1 = new Thread(accountManager1.FundTransfer)
            {
                Name = "Thread1"
            };

            AccountManager accountManager2 = new AccountManager(Account1002, Account1001, 6000);
            Thread thread2 = new Thread(accountManager2.FundTransfer)
            {
                Name = "Thread2"
            };

            thread1.Start();
            thread2.Start();

            thread1.Join();
            thread2.Join();
            Console.WriteLine("Main Thread Completed");
            Console.ReadKey();
        }
    }
}

Output:

Deadlock in C#

Note: For thread1, Account1001 is resource1 and Account1002 is resource2. On the other hand for thread2, Account1002 is the resource1 and Account1001 is resource2. With this keep in mind run the application and see deadlock occurred.

The reason is thread1 acquired an exclusive lock on Account1001 and then do some processing. Meantime thread2 started and it acquired an exclusive lock on Account1002 and then does some processing. Then thread1 back and wants to acquire a lock on Account1001 which is already locked by thread2. Similarly thread2 back and wants to acquire a lock on Account1002 which is already locked by thread1 and hence deadlock.

In the next article, I am going to discuss how to solve the deadlock situation in a multithread application. Here, in this article, I try to explain why and how a deadlock occurs in multithreaded application with an example. I hope you enjoy this article.

Leave a Reply

Your email address will not be published. Required fields are marked *