| 方法 | 說明 |
Enter(object) | 取得物件鎖 (相當於 lock 進入) |
Exit(object) | 釋放物件鎖 (相當於 lock 退出) |
Wait(object) | 釋放鎖並進入等待隊列,被 Pulse 唤醒後重新取得鎖 |
Wait(object, timeout) | 帶逾時等待 |
Pulse(object) | 喚醒等待隊列中第一個執行緒 |
PulseAll(object) | 喚醒等待隊列中所有執行緒 |
TryEnter(object) | 嘗試取得鎖,立即返回成否 |
三、Cell 共享資料物件:雙緩衝旗標同步
public class Cell
{
int _cellContents;
bool _readerFlag; // true = 可讀/已生產, false = 可寫/待生產
public int ReadFromCell()
{
lock (this)
{
if (!_readerFlag) // 尚未生產
{
try { Monitor.Wait(this); }
catch (SynchronizationLockException) { }
catch (ThreadInterruptedException) { }
}
Console.WriteLine($"Consume: {_cellContents}");
_readerFlag = false; // 標記已消費
Monitor.Pulse(this); // 通知生產者可生產
}
return _cellContents;
}
public void WriteToCell(int n)
{
lock (this)
{
if (_readerFlag) // 尚未消費
{
try { Monitor.Wait(this); }
catch (SynchronizationLockException) { }
catch (ThreadInterruptedException) { }
}
_cellContents = n;
Console.WriteLine($"Produce: {_cellContents}");
_readerFlag = true; // 標記已生產
Monitor.Pulse(this); // 通知消費者可消費
}
}
}
四、生產者與消費者類別
public class CellProd
{
readonly Cell _cell;
readonly int _quantity;
public CellProd(Cell cell, int quantity) { _cell = cell; _quantity = quantity; }
public void ThreadRun()
{
for (int i = 1; i <= _quantity; i++)
_cell.WriteToCell(i);
}
}
public class CellCons
{
readonly Cell _cell;
readonly int _quantity;
public CellCons(Cell cell, int quantity) { _cell = cell; _quantity = quantity; }
public void ThreadRun()
{
for (int i = 1; i <= _quantity; i++)
_cell.ReadFromCell();
}
}
五、主程式啟動與同步
public class MonitorSample
{
public static void Main()
{
int result = 0;
var cell = new Cell();
var prod = new CellProd(cell, 20);
var cons = new CellCons(cell, 20);
var producer = new Thread(prod.ThreadRun);
var consumer = new Thread(cons.ThreadRun);
try
{
producer.Start();
consumer.Start();
producer.Join();
consumer.Join();
Console.ReadLine();
}
catch (ThreadStateException e) { Console.WriteLine(e); result = 1; }
catch (ThreadInterruptedException e) { Console.WriteLine(e); result = 1; }
Environment.ExitCode = result;
}
}
六、執行結果與流程分析
Produce: 1
Consume: 1
Produce: 2
Consume: 2
...
Produce: 20
Consume: 20
- 生產者取得鎖,檢查
_readerFlag=false,寫入 1,設 true,Pulse
- 消費者取得鎖,檢查
_readerFlag=true,讀取 1,設 false,Pulse
- 交替進行,嚴格 1:1 對應
七、關鍵設計細節
| 細節 | 作用 |
lock(this) 外層 | 確保 Wait/Pulse 與旗標檢查在同一臨界區 |
_readerFlag 雙緩衝 | 單緩衝無法區分「空」與「滿」狀態 |
Monitor.Wait 釋放鎖 | 避免死結,允許另一方進入 |
Pulse 單一喚醒 | 只有一個對方,PulseAll 會造成虛假喚醒 |
| 例外處理 | SynchronizationLockException (非同步區呼叫 Wait) ThreadInterruptedException (等待中被中斷) |
八、Monitor vs lock vs Semaphore 比較
| 特性 | lock | Monitor | Semaphore/SemaphoreSlim |
| 條件等待 | 無 | Wait/Pulse | 無 (計數信號) |
| 語法糖 | 是 | 否 | 否 |
| 跨進程 | 否 | 否 | 具名 Semaphore 可跨進程 |
| 非同步支援 | 無 | 無 | SemaphoreSlim.WaitAsync |
總結
Monitor 以物件鎖 + 條件變數模式實現精細的執行緒協作。生產者-消費者模式展示 Wait 釋放鎖等待、Pulse 單點喚醒的經典交互模式。掌握 Monitor 可構建資源池、背壓控制、多階段管線等進階同步結構。現代替代方案:BlockingCollection<T> (內建生產消費)、Channels (非同步管線)、SemaphoreSlim (非同步信號)。
- 上一篇文章: C# 多執行緒機制探索(8):ThreadPool 執行緒池與 Timer 定時器自動化管理基礎架構
- 下一篇文章: C# 多執行緒機制探索(6):Monitor 監視器基礎與共享物件同步機制深度解析
Copyright ©
程式碼之家 All Rights Reserved