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

多執行緒死鎖預防指南:鎖層級化與通用多鎖排序輔助類

     編輯:關於.NET

死鎖四必要條件

  1. 互斥:資源一次僅供一執行緒使用
  2. 佔有並等待:持有鎖時申請新鎖
  3. 不可搶佔:鎖只能由持有者釋放
  4. 循環等待:執行緒鏈頭尾相接,各自等待下一個持有的鎖

破壞任一條件即可避免死鎖。第 4 條「循環等待」最容易透過程式設計消除。

經典錯誤:轉帳死鎖

public static void Transfer(Account from, Account to, decimal amount) {
    lock (from) {                // 先鎖 from
        if (from.Balance < amount) throw new Exception("餘額不足");
        lock (to) {              // 再鎖 to
            from.Balance -= amount;
            to.Balance += amount;
        }
    }
}
// 若兩執行緒同時呼叫 Transfer(A,B) 與 Transfer(B,A) → 死鎖

修正一:固定鎖順序(以 ID 排序)

public static void SafeTransfer(Account from, Account to, decimal amount) {
    if (from.Id < to.Id) {
        lock (from) lock (to) DoTransfer(from, to, amount);
    } else if (from.Id > to.Id) {
        lock (to) lock (from) DoTransfer(from, to, amount);
    } // 同 ID 不轉帳
}
private static void DoTransfer(Account from, Account to, decimal amount) { ... }

缺點:僅適用於特定型別,擴充性差。

修正二:通用多鎖排序輔助類

public static class MultiLockHelper<T> where T : IComparable<T> {
    public static void Enter(params T[] locks) {
        Array.Sort(locks);                    // 依 IComparable 排序,建立全域一致順序
        int i = 0;
        try {
            for (; i < locks.Length; i++) Monitor.Enter(locks[i]);
        } catch {
            for (int j = i - 1; j >= 0; j--) Monitor.Exit(locks[j]); // 回滾已取得的鎖
            throw;
        }
    }
    public static void Exit(params T[] locks) {
        Array.Sort(locks);
        for (int i = locks.Length - 1; i >= 0; i--) Monitor.Exit(locks[i]);
    }
}

使用範例

public class Account : IComparable<Account> {
    public int Id { get; set; }
    public decimal Balance { get; set; }
    public int CompareTo(Account other) => Id.CompareTo(other.Id);

    public static void Transfer(Account from, Account to, decimal amount) {
        MultiLockHelper<Account>.Enter(from, to);
        try { DoTransfer(from, to, amount); }
        finally { MultiLockHelper<Account>.Exit(from, to); }
    }
    private static void DoTransfer(Account from, Account to, decimal amount) { ... }
}

鎖層級化的複雜度

  • 需在架構初期規劃鎖層級(如:資料庫鎖 > 快取鎖 > 業務物件鎖)。
  • 專案增長時難以完整掌握所有鎖使用點,易出現違規。
  • 通用排序輔助類將「順序規則」內建於型別的 CompareTo,編譯期即可部分驗證,維護成本較低。

最佳實務摘要

  1. 盡量減少鎖的持有時間與範圍。
  2. 多鎖時統一獲取順序(以不可變識別碼排序)。
  3. 封裝 Enter/Exit 模式,搭配 try/finally 確保釋放。
  4. 避免在鎖內呼叫外部虛擬方法/委派(不可控風險)。
  5. 考慮 ReaderWriterLockSlim、ConcurrentDictionary 等無鎖/細粒度替代方案。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved