Article Categories
- All Categories
-
Data Structure
-
Networking
-
RDBMS
-
Operating System
-
Java
-
MS Excel
-
iOS
-
HTML
-
CSS
-
Android
-
Python
-
C Programming
-
C++
-
C#
-
MongoDB
-
MySQL
-
Javascript
-
PHP
-
Economics & Finance
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 |
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.
