lock(obj) { ... } 展開為 Monitor.Enter/Exit 配合 try/finally。Monitor.Enter 會自動箱裝,但同樣因多次箱裝而失效,實務上皆應鎖定專用參考型別物件。Wait、Pulse、PulseAll、TryEnter 等,適用於生產者/消費者等複雜同步場景。| 方法/屬性 | 說明 |
|---|---|
Enter(object) | 取得物件排他鎖,阻塞至取得為止。 |
Exit(object) | 釋放鎖。 |
TryEnter(object) / TryEnter(object, ref bool) | 嘗試取得鎖,立即返回成功與否,不阻塞。 |
Wait(object) | 釋放鎖並阻塞當前執行緒,直到被 Pulse 喚醒並重新取得鎖。 |
Pulse(object) / PulseAll(object) | 喚醒一個/所有等待佇列中的執行緒。 |
IsEntered | 檢查當前執行緒是否持有該物件鎖。 |
// 原始碼
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) { } // 值型別每次箱裝不同物件,完全失效
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 喚醒後自動重新取得鎖,是條件變數的標準實作。
lock**:語法簡潔、編譯器保護、可讀性高,涵蓋 95% 鎖定需求。Monitor。this、公開型別。