破壞任一條件即可避免死鎖。第 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) → 死鎖
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,編譯期即可部分驗證,維護成本較低。Enter/Exit 模式,搭配 try/finally 確保釋放。ReaderWriterLockSlim、ConcurrentDictionary 等無鎖/細粒度替代方案。