Deadlock
Let's see what it is, so we can avoid it!
namespace Concurrent;
public class DeadlockDemo
{
private readonly object _lockA = new();
private readonly object _lockB = new();
public void CauseDeadlock()
{
// Thread 1: Grabs Lock A, then tries for Lock B
var thread1 = new Thread(() =>
{
lock (_lockA)
{
Console.WriteLine("Thread 1: Acquired lock A. Waiting for lock B...");
// Small delay to ensure Thread 2 has time to grab lock B
Thread.Sleep(500);
lock (_lockB)
{
Console.WriteLine("Thread 1: Acquired lock B!");
}
}
});
// Thread 2: Grabs Lock B, then tries for Lock A
var thread2 = new Thread(() =>
{
lock (_lockB)
{
Console.WriteLine("Thread 2: Acquired lock B. Waiting for lock A...");
// Small delay to ensure Thread 1 has time to grab lock A
Thread.Sleep(100);
lock (_lockA)
{
Console.WriteLine("Thread 2: Acquired lock A!");
}
}
});
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
Console.WriteLine("This line will never be reached.");
}
}
Why this happens:
- Thread 1 enters _lockA.
- Thread 2 enters _lockB.
- Thread 1 tries to enter _lockB, but it’s held by Thread 2, so it waits.
- Thread 2 tries to enter _lockA, but it’s held by Thread 1, so it waits.
- They are now stuck in a cycle where neither can move forward.
How to avoid this in real applications:
- Consistency: Always acquire locks in the same order (e.g., always _lockA then _lockB).
- Timeouts: Use Monitor.TryEnter(lockObject, timeout) instead of the lock keyword.
- Granularity: Keep your locked sections as small as possible and avoid performing I/O or calling external methods while holding a lock if possible.
No files yet, migration hasn't completed yet!