Deadlock and Starvation in C#

Deadlock occurs when two or more threads are permanently blocked, each waiting for a resource held by another thread. This creates a circular dependency where no thread can proceed, making it a critical problem in multithreaded applications.

Starvation happens when a thread is indefinitely denied access to resources it needs, typically because other threads with higher priority continuously monopolize those resources.

Understanding Deadlock

A deadlock situation arises when threads hold locks and wait for other locks in a circular chain. The classic scenario involves two threads and two resources −

Thread One Thread Two
Takes Lock A Takes Lock B
Requests Lock B Requests Lock A
Waits indefinitely Waits indefinitely

Deadlock Scenario Thread 1 Thread 2 Lock A Lock B Waiting for each other's lock Holds Holds

Example of Deadlock

using System;
using System.Threading;

class DeadlockExample {
   private static object lockA = new object();
   private static object lockB = new object();

   static void Main() {
      Thread thread1 = new Thread(Method1);
      Thread thread2 = new Thread(Method2);

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

      // Wait briefly to let deadlock occur
      Thread.Sleep(3000);
      Console.WriteLine("Program may be deadlocked - check task manager");
   }

   static void Method1() {
      lock (lockA) {
         Console.WriteLine("Thread 1: Acquired Lock A");
         Thread.Sleep(1000);
         
         Console.WriteLine("Thread 1: Waiting for Lock B");
         lock (lockB) {
            Console.WriteLine("Thread 1: Acquired Lock B");
         }
      }
   }

   static void Method2() {
      lock (lockB) {
         Console.WriteLine("Thread 2: Acquired Lock B");
         Thread.Sleep(1000);
         
         Console.WriteLine("Thread 2: Waiting for Lock A");
         lock (lockA) {
            Console.WriteLine("Thread 2: Acquired Lock A");
         }
      }
   }
}

The output of the above code shows the deadlock occurring −

Thread 1: Acquired Lock A
Thread 2: Acquired Lock B
Thread 1: Waiting for Lock B
Thread 2: Waiting for Lock A
Program may be deadlocked - check task manager

Preventing Deadlock

The most effective way to prevent deadlock is to acquire locks in the same order across all threads −

Example of Deadlock Prevention

using System;
using System.Threading;

class DeadlockPrevention {
   private static object lockA = new object();
   private static object lockB = new object();

   static void Main() {
      Thread thread1 = new Thread(Method1);
      Thread thread2 = new Thread(Method2);

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

      thread1.Join();
      thread2.Join();
      Console.WriteLine("Both threads completed successfully");
   }

   static void Method1() {
      // Always acquire lockA first, then lockB
      lock (lockA) {
         Console.WriteLine("Thread 1: Acquired Lock A");
         Thread.Sleep(500);
         
         lock (lockB) {
            Console.WriteLine("Thread 1: Acquired Lock B");
            Thread.Sleep(500);
         }
         Console.WriteLine("Thread 1: Released both locks");
      }
   }

   static void Method2() {
      // Same order: lockA first, then lockB
      lock (lockA) {
         Console.WriteLine("Thread 2: Acquired Lock A");
         Thread.Sleep(500);
         
         lock (lockB) {
            Console.WriteLine("Thread 2: Acquired Lock B");
            Thread.Sleep(500);
         }
         Console.WriteLine("Thread 2: Released both locks");
      }
   }
}

The output of the above code shows successful execution without deadlock −

Thread 1: Acquired Lock A
Thread 1: Acquired Lock B
Thread 1: Released both locks
Thread 2: Acquired Lock A
Thread 2: Acquired Lock B
Thread 2: Released both locks
Both threads completed successfully

Understanding Starvation

Starvation occurs when a thread is perpetually denied access to resources. This typically happens in priority-based scheduling where high-priority threads continuously prevent low-priority threads from executing.

Example of Thread Starvation

using System;
using System.Threading;

class StarvationExample {
   private static object sharedResource = new object();
   private static bool keepRunning = true;

   static void Main() {
      // High priority thread
      Thread highPriorityThread = new Thread(HighPriorityWork);
      highPriorityThread.Priority = ThreadPriority.Highest;
      
      // Low priority thread (may starve)
      Thread lowPriorityThread = new Thread(LowPriorityWork);
      lowPriorityThread.Priority = ThreadPriority.Lowest;

      highPriorityThread.Start();
      lowPriorityThread.Start();

      Thread.Sleep(5000);
      keepRunning = false;

      highPriorityThread.Join();
      lowPriorityThread.Join();
   }

   static void HighPriorityWork() {
      int count = 0;
      while (keepRunning) {
         lock (sharedResource) {
            count++;
            if (count % 1000 == 0) {
               Console.WriteLine($"High priority thread executed {count} times");
            }
         }
      }
   }

   static void LowPriorityWork() {
      int count = 0;
      while (keepRunning) {
         lock (sharedResource) {
            count++;
            if (count % 100 == 0) {
               Console.WriteLine($"Low priority thread executed {count} times");
            }
         }
      }
   }
}

The output demonstrates how the low-priority thread may get significantly fewer execution opportunities −

High priority thread executed 1000 times
High priority thread executed 2000 times
Low priority thread executed 100 times
High priority thread executed 3000 times
High priority thread executed 4000 times
Low priority thread executed 200 times

Deadlock vs Starvation

Deadlock Starvation
Complete halt of execution Delayed or limited execution
Affects multiple threads Usually affects one thread
Circular wait condition Priority or scheduling issue
Requires external intervention May resolve over time

Conclusion

Deadlock creates a complete standstill where threads wait indefinitely for each other's resources, while starvation causes indefinite delays for specific threads. Both can be prevented through careful lock ordering, timeout mechanisms, and fair scheduling policies in multithreaded applications.

Updated on: 2026-03-17T07:04:35+05:30

2K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements