一、生產者-消費者問題本質
共享資料結構(佇列),生產者 Enqueue,消費者 Dequeue。核心挑戰:
- 互斥存取:同一時間只能一個執行緒修改佇列
- 條件同步:佇列空時消費者等待,滿時生產者等待 (有界佇列)
二、lock 關鍵字:互斥區塊語法糖
lock (expression) statement_block
// 編譯為
Monitor.Enter(expression);
try { statement_block; }
finally { Monitor.Exit(expression); }
expression 選擇指南
| 保護目標 | expression | 範例 |
| 實例成員 | this | lock(this) |
| 靜態成員 | 型別本身 | lock(typeof(MyClass)) |
| 共享物件 | 該物件實例 | lock(_sharedQueue) |
| 專用鎖物件 | private readonly object | lock(_syncRoot) (推薦) |
三、經典錯誤:檢查-執行競態
// 錯誤版本
if (balance >= amount) // 檢查
{
Thread.Sleep(5); // 模擬延遲
balance = balance - amount; // 執行:此時 balance 可能已被其他執行緒修改
}
// 正確版本:lock 保護整個檢查-執行序列
lock (this)
{
if (balance >= amount)
{
Thread.Sleep(5);
balance -= amount;
return amount;
}
return 0;
}
四、完整帳戶轉帳範例
using System;
using System.Threading;
internal class Account
{
int _balance;
Random _r = new Random();
internal Account(int initial) { _balance = initial; }
internal int Withdraw(int amount)
{
if (_balance < 0) throw new Exception("Negative Balance");
lock (this)
{
Console.WriteLine($"Current Thread: {Thread.CurrentThread.Name}");
if (_balance >= amount)
{
Thread.Sleep(5);
_balance -= amount;
return amount;
}
return 0; // 拒絕交易
}
}
internal void DoTransactions()
{
for (int i = 0; i < 100; i++)
Withdraw(_r.Next(-50, 100));
}
}
internal class Test
{
static Thread[] _threads = new Thread[10];
public static void Main()
{
var acc = new Account(0);
for (int i = 0; i < 10; i++)
{
var t = new Thread(new ThreadStart(acc.DoTransactions));
_threads[i] = t;
t.Name = i.ToString();
}
for (int i = 0; i < 10; i++) _threads[i].Start();
Console.ReadLine();
}
}
五、lock 進階特性
- 重進入:同一執行緒可多次
lock 同一物件
- 不可中斷:
lock 等待不可被 Thread.Interrupt 中斷
- 無逾時:會無限等待,死結風險較高
- 效能:非爭用極快 (CAS 自旋),爭用進入核心等待
六、lock 適用與不適用場景
| 適用 | 不適用 |
| 簡單互斥保護 | 需條件等待 (用 Monitor.Wait/Pulse) |
| 短小關鍵區 | 長時間阻塞操作 (I/O、Sleep) |
| 實例/靜態成員保護 | 跨進程同步 (用 Mutex) |
| 非同步等待 | 需非同步等待 (用 SemaphoreSlim.WaitAsync) |
總結
lock 是 C# 最常用的同步原語,將 Monitor.Enter/Exit 包裝為例外安全的語法糖。核心原則:保護檢查-執行原子性、選擇正確鎖物件、關鍵區越小越好。生產者-消費者需條件同步時,需進階至 Monitor.Wait/Pulse、BlockingCollection、Channels 等高階抽象。
- 上一篇文章: C# 多執行緒機制探索(6):Monitor 監視器基礎與共享物件同步機制深度解析
- 下一篇文章: C# 多執行緒機制探索(四):執行緒狀態、優先權、前景/背景執行緒與生命週期
Copyright ©
程式碼之家 All Rights Reserved