在多線程程式設計中,協調多個線程的執行順序與資源存取是核心課題。Windows 8 Store Apps 提供了豐富的同步原語,本文將詳細介紹 Semaphore、CountdownEvent、Barrier、ManualResetEvent 以及 AutoResetEvent 的運作原理與實際應用場景。
信號量 可視為「許可證中心」,限制同時存取共享資源的線程數量。SemaphoreSlim 是輕量級版本,適合單一進程內的高頻率同步。
// 建立擁有 2 個許可證的信號量
var semaphore = new SemaphoreSlim(2, 2);
for (int i = 0; i < 5; i++) {
Task.Run(async () => {
await semaphore.WaitAsync(); // 非阻塞等待許可證
try {
// 只有 2 個線程能同時進入此區塊
await ProcessResourceAsync();
} finally {
semaphore.Release(); // 歸還許可證
}
});
}
應用場景: 連線池限流、並發檔案 I/O 控制、API 速率限制。
CountdownEvent 透過信號計數實現「等待 N 個操作完成」的模式。初始計數為 N,每次 Signal() 遞減,當歸零時釋放等待線程。
using (var countdown = new CountdownEvent(100)) {
for (int i = 0; i < 100; i++) {
Task.Run(() => {
DoWork();
countdown.Signal(); // 完成一項工作
});
}
countdown.Wait(); // 阻塞直到 100 項全完成
}
優勢: 比手動維護計數器配合 Monitor.Wait/Pulse 更簡潔、不易出錯。
Barrier 適用於分階段運算的平行演算法。所有參與者呼叫 SignalAndWait() 到達屏障,最後一個到達時觸發階段結束動作(可選),然後全部釋放進入下一階段。
var barrier = new Barrier(5, b => {
// 所有 5 個參與者到達後執行(可選的階段後動作)
Console.WriteLine($"Phase {b.CurrentPhaseNumber} completed");
});
Parallel.For(0, 5, i => {
Phase1Work();
barrier.SignalAndWait(); // 等待其他參與者
Phase2Work();
barrier.SignalAndWait();
});
動態調整: 支援 AddParticipant/RemoveParticipant 在運行時改變參與者數量。
「手動」意指必須顯式呼叫 Reset() 切換為紅燈(非發信狀態),Set() 切換為綠燈(發信狀態)。綠燈期間所有等待者皆可通過。
var mre = new ManualResetEventSlim(false); // 初始紅燈
// 生產者
Task.Run(() => {
PrepareData();
mre.Set(); // 亮綠燈,通知所有消費者
});
// 多個消費者
for (int i = 0; i < 3; i++) {
Task.Run(() => {
mre.Wait(); // 紅燈阻塞,綠燈通過
ConsumeData();
});
}
// 注意:必須手動 mre.Reset() 才會重新變紅燈
「自動」意指單一等待者通過 WaitOne() 後,事件會自動重置為非發信狀態(紅燈)。適合「一對一」通知模式。
var are = new AutoResetEvent(false);
Task.Run(() => {
for (int i = 0; i < 10; i++) {
ProduceItem(i);
are.Set(); // 喚醒一個消費者
}
});
Task.Run(() => {
for (int i = 0; i < 10; i++) {
are.WaitOne(); // 每次只有一個線程被喚醒
ConsumeItem();
}
});
| 同步原語 | 核心語意 | 典型用途 | 關鍵特性 |
|---|---|---|---|
| Semaphore/Slim | 許可證計數 | 資源池限流 | 計數型、支援非同步 WaitAsync |
| CountdownEvent | 倒數歸零 | 等待 N 個任務完成 | 一次性、不可重置 |
| Barrier | 階段屏障 | 分階段平行演算法 | 可重用、支援動態參與者、階段後回呼 |
| ManualResetEvent/Slim | 手動切換狀態 | 廣播通知、狀態旗標 | 需手動 Reset、多線程同步通過 |
| AutoResetEvent | 自動重置 | 單一消費者喚醒 | WaitOne 後自動變紅燈、一對一通知 |
Task.WhenAll、SemaphoreSlim.WaitAsync、Barrier 等現代 API,避免手工管理底層事件。WaitAsync 而非同步 Wait 以免阻塞 UI 線程。SemaphoreSlim、ManualResetEventSlim,效能更優。IDisposable 的同步物件(Semaphore、CountdownEvent、Barrier)請在 using 區塊或 finally 中釋放。熟練掌握這些同步機制,能讓你優雅地解決 Windows 8 Store Apps 中複雜的多線程協調問題。