本系列面向初學者,歡迎批評指教。程式碼涉及委派概念。
四條件同時滿足必發生死鎖;破壞任一條件即可避免。
using System;
using System.Threading;
class DeadlockDemo
{
static object lockA = new object();
static object lockB = new object();
static void Main()
{
Thread t1 = new Thread(Thread1Work);
Thread t2 = new Thread(Thread2Work);
t1.Start();
t2.Start();
t1.Join();
t2.Join();
Console.WriteLine("程式結束(若發生死鎖永不會執行到這裡)");
}
static void Thread1Work()
{
lock (lockA)
{
Console.WriteLine("線程 1 取得 lockA");
Thread.Sleep(100); // 增加死鎖機率
lock (lockB)
{
Console.WriteLine("線程 1 取得 lockB");
}
}
}
static void Thread2Work()
{
lock (lockB)
{
Console.WriteLine("線程 2 取得 lockB");
Thread.Sleep(100);
lock (lockA)
{
Console.WriteLine("線程 2 取得 lockA");
}
}
}
}
| 策略 | 對應破壞條件 | 做法 |
|---|---|---|
| 固定鎖順序 | 循環等待 | 所有線程按相同順序取得鎖(如先 lockA 再 lockB) |
| 鎖超時機制 | 佔有並等待 | 使用 Monitor.TryEnter(timeout),超時則釋放已持有鎖並重試 |
| 避免巢狀鎖 | 佔有並等待 | 重構程式碼,減少同時持有多個鎖的情況 |
| 使用較高層級同步 | 多重條件 | 改用 SemaphoreSlim、ReaderWriterLockSlim、Concurrent 集合等 |
| 死鎖偵測 | 事後處理 | 定期檢查線程等待圖,發現循環則中斷(複雜,較少用) |
static void Thread2Work_Fixed()
{
// 統一按 lockA → lockB 順序
lock (lockA)
{
Console.WriteLine("線程 2 取得 lockA");
Thread.Sleep(100);
lock (lockB)
{
Console.WriteLine("線程 2 取得 lockB");
}
}
}
static void SafeWork()
{
bool gotA = false, gotB = false;
try
{
gotA = Monitor.TryEnter(lockA, 1000);
if (!gotA) { Console.WriteLine("取得 lockA 超時"); return; }
gotB = Monitor.TryEnter(lockB, 1000);
if (!gotB) { Console.WriteLine("取得 lockB 超時"); return; }
// 臨界區程式碼
}
finally
{
if (gotB) Monitor.Exit(lockB);
if (gotA) Monitor.Exit(lockA);
}
}
(原文包含四張圖片,展示死鎖形成過程、等待圖、避免策略圽解等)