在 Windows 8 Store Apps 開發中,多線程程式設計是提升應用程式回應性與效能的關鍵。除了基礎的鎖機制與 Task 平行庫外,.NET Framework 還提供了一組專門的進階輔助類別,用於處理特定的並發場景。本文將深入探討 SpinWait、SpinLock、volatile 關鍵字、SynchronizationContext、CoreDispatcher、ThreadLocal<T> 以及 ThreadStaticAttribute 的實際應用與最佳實踐。
SpinWait 是一個低層級的同步原語,適合在多核心 CPU 環境下、預期等待時間極短(微秒級)的場景。它不會釋放 CPU 時間片,而是透過忙碌循環等待條件成立,避免了線程上下文切換的開銷。
// 等待條件成立,最多等待 1000 毫秒
SpinWait.SpinUntil(() => DateTime.Now.Second % 2 == 0, 1000);
適用場景: 當你知道鎖持有時間極短,且不希望線程進入核心模式等待時。
SpinLock 同樣採用忙碌等待策略,適合保護極小臨界區的情況。以下範例展示 100 個平行任務安全地對共享計數器進行遞增:
SpinLock spinLock = new SpinLock();
int count = 0;
Parallel.For(0, 100, i => {
bool lockTaken = false;
try {
spinLock.Enter(ref lockTaken);
for (int j = 0; j < 100000; j++) count++;
} finally {
if (lockTaken) spinLock.Exit();
}
});
注意: 若臨界區執行時間較長,應改用 lock 或 Monitor 以避免 CPU 資源浪費。
volatile 告訴編譯器該欄位可能被外部(如其他線程、硬體、後台任務)修改,強制從主記憶體讀寫而非暫存器。在 Windows 執行緒集區或背景任務修改共享欄位時,標記為 volatile 可防止編譯器優化導致的資料不一致。
// 後台任務可能修改此旗標,必須標記為 volatile
private volatile bool _isCancelled;
SynchronizationContext.Current 捕獲 UI 線程的同步內文,透過 Post 方法將委派排程至 UI 線程執行,適用於傳統 .NET 與 WinRT 通用的跨線程回呼。
var uiContext = SynchronizationContext.Current;
ThreadPoolTimer.CreatePeriodicTimer(timer => {
uiContext.Post(_ => lblMsg.Text = DateTime.Now.ToString("mm:ss.fff"), null);
}, TimeSpan.FromMilliseconds(100));
CoreDispatcher 是 Windows Runtime 專屬的 UI 執行緒調度器,提供更精細的優先級控制(CoreDispatcherPriority),是 WinRT 應用中推薦的跨線程 UI 更新方式。
var dispatcher = Window.Current.Dispatcher;
ThreadPoolTimer.CreatePeriodicTimer(timer => {
dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
lblMsg.Text = DateTime.Now.ToString("mm:ss.fff");
});
}, TimeSpan.FromMilliseconds(100));
ThreadLocal<T> 提供線程隔離的資料儲存,支援延遲初始化與追蹤所有線程的值。每個線程存取 Value 時獲取屬於自己的獨立副本。
var localData = new ThreadLocal(() => "UI Thread Data", trackAllValues: false);
Task.Run(() => localData.Value = "Worker 1 Data");
Task.Run(() => localData.Value = "Worker 2 Data");
// UI 線程讀取自己的值
Console.WriteLine(localData.Value); // "UI Thread Data"
標記 [ThreadStatic] 的靜態欄位,每個線程擁有獨立的儲存空間,初始值為預設值(數值型為 0,參考型為 null)。較 ThreadLocal<T> 輕量,但不支援自訂初始化邏輯。
[ThreadStatic] private static int _threadLocalCounter;
Task.Run(() => _threadLocalCounter = 10);
Task.Run(() => _threadLocalCounter = 100);
// UI 線程的值仍為 0
Console.WriteLine(_threadLocalCounter); // 0
| 類別/關鍵字 | 核心用途 | 適用場景 |
|---|---|---|
| SpinWait | 忙碌等待條件 | 極短暫等待、無阻塞輪詢 |
| SpinLock | 輕量互斥鎖 | 極小臨界區、高併發低爭用 |
| volatile | 記憶體可見性 | 跨線程/外部修改的共享欄位 |
| SynchronizationContext | 通用 UI 同步 | 跨平台程式碼、傳統 .NET 相容 |
| CoreDispatcher | WinRT UI 調度 | Windows 8/10 Store Apps 首選 |
| ThreadLocal<T> | 強型別線程區域儲存 | 需自訂初始化、追蹤所有線程值 |
| ThreadStaticAttribute | 靜態欄位線程隔離 | 簡單計數器、無需初始化邏輯 |
掌握這些進階輔助類別,能讓你在 Windows 8 Store Apps 中針對不同並發需求選擇最適合的同步策略,兼顧效能與正確性。