C# 的 ManualResetEvent 類詳解
作用
ManualResetEvent
?是用于線程同步操作的類,允許一個或多個線程等待特定信號,以協調多個線程的執行順序。它通過事件通知機制實現,確保線程在收到信號前保持阻塞,直到其他線程顯式發出信號。
核心功能
-
阻塞線程:調用?
WaitOne()
?使線程進入等待狀態。 -
發送信號:調用?
Set()
?將事件設為終止狀態,釋放所有等待線程。 -
重置信號:調用?
Reset()
?將事件恢復為非終止狀態。
信號狀態:
- 終止狀態:所有調用?
WaitOne(),
線程不會被阻塞,直到調用Reset()。 - 非終止狀態:所有調用?
WaitOne(),
線程會被阻塞,直到調用set()。
特點
-
手動重置:調用?
Set()
?后需手動調用?Reset()
?才能將狀態恢復為非終止狀態。 -
多線程釋放:一旦處于終止狀態(Signaled),所有等待線程立即釋放,直到手動重置。
-
線程安全:所有方法(
Set
、Reset
、WaitOne
)都是線程安全的。 -
跨進程支持:可通過命名方式在進程間同步(構造函數傳名稱)。
應用場景
-
初始化同步:主線程等待子線程完成初始化后再繼續。
-
資源就緒通知:多個工作線程等待某個共享資源(如數據加載完成)。
-
階段化任務:分階段任務中,后續階段需等待前一階段所有線程完成。
-
高并發控制:替代鎖機制,允許多個線程同時訪問資源(需配合?
Reset()
)。
基礎用法
-
初始化:創建實例,參數指定初始狀態(
true
?為終止狀態)。ManualResetEvent mre = new ManualResetEvent(false); // 初始為非終止
-
阻塞線程:調用?
WaitOne()
。mre.WaitOne(); // 阻塞,直到事件變為終止狀態
-
發送信號:調用?
Set()
。mre.Set(); // 設置為終止狀態,釋放所有等待線程
-
重置信號:調用?
Reset()
。mre.Reset(); // 恢復為非終止狀態
代碼實例
場景 1:主線程等待子線程完成
用途
主線程需要等待子線程完成某個任務后再繼續執行。例如:主線程啟動后臺任務后需等待其初始化完成,再執行后續操作。
代碼邏輯
class Example
{static ManualResetEvent mre = new ManualResetEvent(false);static void Main(){Console.WriteLine("主線程啟動工作線程。");Thread worker = new Thread(DoWork);worker.Start();Console.WriteLine("主線程等待信號...");mre.WaitOne(); // 阻塞主線程,直到子線程調用 Set()Console.WriteLine("主線程恢復執行。");}static void DoWork(){Console.WriteLine("工作線程執行任務...");Thread.Sleep(2000); // 模擬耗時操作mre.Set(); // 發送信號,喚醒主線程}
}
執行流程
-
主線程創建?
ManualResetEvent
?并初始化為非終止狀態 (false
)。 -
主線程啟動子線程?
DoWork
。 -
主線程調用?
mre.WaitOne()
?進入阻塞狀態。 -
子線程執行任務(模擬耗時操作),完成后調用?
mre.Set()
,將事件狀態設為終止。 -
主線程從?
WaitOne()
?處解除阻塞,繼續執行后續代碼。
輸出結果
主線程啟動工作線程。 主線程等待信號... 工作線程執行任務... (等待 2 秒后) 主線程恢復執行。
場景 2:多個線程等待同一事件
用途
多個工作線程需要等待某個公共條件(如資源初始化完成)滿足后,才能同時開始工作。例如:多個線程需等待數據庫連接池初始化完成后才可執行查詢。
代碼邏輯
using System;
using System.Threading;class Example
{static ManualResetEvent mre = new ManualResetEvent(false);static void Main(){// 啟動 3 個工作線程for (int i = 0; i < 3; i++){new Thread(Worker).Start(i);}// 啟動初始化線程new Thread(Initialize).Start();}static void Initialize(){Console.WriteLine("初始化開始...");Thread.Sleep(3000);Console.WriteLine("初始化完成!");mre.Set(); // 通知所有等待線程}static void Worker(object id){Console.WriteLine($"線程 {id} 等待初始化...");mre.WaitOne(); // 阻塞,直到初始化線程調用 Set()Console.WriteLine($"線程 {id} 開始工作。");}
}
執行流程
-
主線程啟動 3 個工作線程和一個初始化線程。
-
每個工作線程調用?
mre.WaitOne()
?進入阻塞狀態,等待初始化完成。 -
初始化線程執行耗時操作(如加載配置),完成后調用?
mre.Set()
。 -
所有等待的工作線程同時被喚醒,開始執行后續任務。
輸出結果
線程 0 等待初始化... 線程 1 等待初始化... 線程 2 等待初始化... 初始化開始... (等待 3 秒后) 初始化完成! 線程 0 開始工作。 線程 1 開始工作。 線程 2 開始工作。
場景 3:重復使用 ManualResetEvent
用途
需要多次復用同一個?ManualResetEvent
?實例,分階段同步多個任務。例如:分批次處理數據,每批任務完成后觸發下一批任務。
代碼邏輯
using System;
using System.Threading;class Example
{static ManualResetEvent mre = new ManualResetEvent(false);static void Main(){// 首次使用new Thread(() => Task("任務1")).Start();mre.WaitOne();mre.Reset(); // 重置為非終止狀態// 第二次使用new Thread(() => Task("任務2")).Start();mre.WaitOne();}static void Task(string name){Console.WriteLine($"{name} 進行中...");Thread.Sleep(1000);mre.Set();}
}
執行流程
-
主線程啟動第一個子線程執行?
任務1
。 -
主線程調用?
mre.WaitOne()
?阻塞,等待?任務1
?完成。 -
子線程?
任務1
?完成后調用?mre.Set()
,主線程恢復執行。 -
主線程調用?
mre.Reset()
?將事件重置為非終止狀態。 -
主線程啟動第二個子線程執行?
任務2
,再次調用?mre.WaitOne()
?阻塞。 -
子線程?
任務2
?完成后調用?mre.Set()
,主線程恢復執行。
輸出結果
任務1 進行中... (等待 1 秒后) 任務2 進行中... (等待 1 秒后)
三個場景關鍵區別總結
場景 | 核心目的 | ManualResetEvent 操作要點 |
---|---|---|
主線程等待子線程 | 單向等待子線程完成 | 子線程完成時調用?Set() |
多線程等待同一事件 | 廣播式喚醒所有等待線程 | Set() ?后無需立即?Reset() |
重復使用事件對象 | 分階段同步任務 | 每次使用后需調用?Reset() ?重置狀態 |
通過這三個場景,可以靈活掌握?ManualResetEvent
?在不同線程同步需求中的使用技巧。