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

C# Monitor 與 lock 關鍵字深度解析:異同、最佳實踐與陷阱

     編輯:關於C#

核心結論先行

  • lock 是 Monitor 的語法糖:編譯器將 lock(obj) { ... } 展開為 Monitor.Enter/Exit 配合 try/finally。
  • lock 僅支援參考型別:值型別會導致每次箱裝成不同物件,失去同步效果。
  • Monitor 支援值型別:Monitor.Enter 會自動箱裝,但同樣因多次箱裝而失效,實務上皆應鎖定專用參考型別物件。
  • Monitor 提供進階功能:Wait、Pulse、PulseAll、TryEnter 等,適用於生產者/消費者等複雜同步場景。

Monitor 核心 API

方法/屬性說明
Enter(object)取得物件排他鎖,阻塞至取得為止。
Exit(object)釋放鎖。
TryEnter(object) / TryEnter(object, ref bool)嘗試取得鎖,立即返回成功與否,不阻塞。
Wait(object)釋放鎖並阻塞當前執行緒,直到被 Pulse 喚醒並重新取得鎖。
Pulse(object) / PulseAll(object)喚醒一個/所有等待佇列中的執行緒。
IsEntered檢查當前執行緒是否持有該物件鎖。

lock 編譯後的真實面貌


// 原始碼
lock (obj) { DoSomething(); }

// 編譯器產生 (概念性)
bool lockTaken = false;
try
{
    Monitor.Enter(obj, ref lockTaken);
    DoSomething();
}
finally
{
    if (lockTaken) Monitor.Exit(obj);
}

.NET 4.0+ 採用 Monitor.Enter(obj, ref lockTaken) 多載,確保即使在 Enter 內部拋出異常 (如 ThreadAbortException),lockTaken 狀態仍正確,finally 才會釋放鎖,避免死結。

鎖定物件的鐵律

// ✅ 正確:私有靜態唯讀物件
private static readonly object _locker = new object();

// ❌ 錯誤示範
lock (this) { }           // 公開實例,外部可能鎖定導致死結
lock (typeof(MyClass)) { } // 公開型別,跨 AppDomain 可能衝突
lock ("string") { }       // 字串暫存池導致非預期共享
lock (123) { }            // 值型別每次箱裝不同物件,完全失效

Monitor 進階應用:生產者/消費者範本

private readonly object _gate = new object();
private readonly Queue<Task> _queue = new Queue<Task>();

public void Enqueue(Task task)
{
    lock (_gate) { _queue.Enqueue(task); Monitor.Pulse(_gate); }
}

public Task Dequeue()
{
    lock (_gate)
    {
        while (_queue.Count == 0) Monitor.Wait(_gate);
        return _queue.Dequeue();
    }
}

Wait 原子性釋放鎖並休眠,被 Pulse 喚醒後自動重新取得鎖,是條件變數的標準實作。

總結建議

  1. **優先使用 lock**:語法簡潔、編譯器保護、可讀性高,涵蓋 95% 鎖定需求。
  2. **需要非阻塞嘗試、超時等待、條件喚醒** 時,直接使用 Monitor。
  3. **永遠鎖定私有唯讀參考型別物件**,避免值型別、字串、this、公開型別。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved