.NET 提供兩種事件訊號類別:ManualResetEvent(手動重設)與 AutoResetEvent(自動重設)。兩者皆繼承自 WaitHandle,底層對應 Win32 CreateEvent API,差別僅在第二個參數 bManualReset 為 true 或 false。核心差異在於:訊號狀態被重設為非訊號狀態的時機由誰控制。
開獎前皆為「無訊號狀態」(彩民等待);開獎瞬間發出「有訊號」(號碼出爐),彩民得知結果後各自散去(執行續被喚醒)。
特性:Set() 後訊號持續保持「有訊號」狀態,直到程式碼明確呼叫 Reset() 才歸零。所有等待的執行緒同時被釋放。
var mre = new ManualResetEvent(false);
// 三個執行緒等待
new Thread(A).Start(); new Thread(B).Start(); new Thread(C).Start();
mre.Set(); // 發訊號:三個執行緒全數喚醒
// 若不呼叫 Reset(),後續新增的執行緒 D 也不會阻塞
new Thread(D).Start(); // 直接跑過 WaitOne()
mre.Reset(); // 手動歸零
new Thread(D).Start(); // 此時會阻塞
mre.Set(); // 再次發訊號喚醒 D
適用場景:單次觸發、多執行緒同步啟動、狀態旗標(如「資料已載入完成」)。
特性:Set() 釋放單一等待執行緒(由 OS 排程器決定),隨後立即自動重設為無訊號狀態。其餘執行緒持續阻塞。
var are = new AutoResetEvent(false);
new Thread(A).Start(); new Thread(B).Start(); new Thread(C).Start();
are.Set(); // 僅喚醒 A、B、C 中隨機一個,其餘仍阻塞
// 若在執行緒內再次呼叫 Set(),可喚醒下一個
// 否則需外部再次呼叫 Set()
適用場景:生產者/消費者佇列、互斥存取、逐項任務分派。
| 特性 | ManualResetEvent | AutoResetEvent |
|---|---|---|
| 訊號持續時間 | 直到手動 Reset() | 喚醒一個執行緒後自動歸零 |
| 喚醒執行緒數 | 所有等待者 | 僅一個(隨機) |
| 類比 | 大門敞開,所有人可通行 | 單人旋轉門,一人通過即鎖上 |
| 典型用途 | 廣播通知、階段性同步 | 資源互斥、工作佇列消費 |
文章附上兩個完整 Console 應用程式範例,分別示範 ManualResetEvent 與 AutoResetEvent 的行為差異。建議實際執行觀察輸出順序與阻塞情況,加深體會。
選擇原則簡單:需一次通知多個執行緒、或狀態需持久保持 → ManualResetEvent;需逐個喚醒處理、互斥存取 → AutoResetEvent。理解「誰負責重設訊號」是掌握兩者本質的關鍵。