首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

多線程入門系列(十):死鎖原理與避免策略

     編輯:關於C#

21.7 死鎖

本系列面向初學者,歡迎批評指教。程式碼涉及委派概念。

死鎖四個必要條件(Coffman Conditions)

  1. 互斥:資源一次只能被一個線程佔用。
  2. 佔有並等待:線程持有資源同時請求新資源。
  3. 非搶佔:資源只能由持有者釋放,不可強制搶佔。
  4. 循環等待:形成線程-資源循環鏈(T1→R1→T2→R2→T1)。

四條件同時滿足必發生死鎖;破壞任一條件即可避免。

經典死鎖範例

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");
        }
    }
}

Monitor.TryEnter 超時範例

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);
    }
}

圖示說明

(原文包含四張圖片,展示死鎖形成過程、等待圖、避免策略圽解等)

死鎖形成示意圖 1 死鎖形成示意圖 2 避免死鎖策略圖 死鎖偵測與解決
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved