前言
多執行緒能提升應用程式回應性與吞吐量,但也帶來競態條件、死結、效能損耗等挑戰。本文從「何時使用」、「如何保證安全」、「如何寫出安全程式碼」、「集合執行緒安全」、「同步機制選擇」、「IIS 架構下的多執行緒」、「有效使用原則」及「執行緒池應用」八個維度深度剖析。
一、何時使用多執行緒?
- 耗時作業(I/O、運算)不阻塞主執行緒(UI 回應)。
- 多核 CPU 環境下並行處理同類任務提高吞吐量。
- 單一任務可拆解為多個獨立子任務並行執行。
二、如何保證執行緒安全?
核心在於識別臨界資源(共享可變狀態)。若多執行緒競爭同一資源且無同步機制,將導致資料損壞或死結。解決之道:避免共享狀態、使用不可變物件、正確應用同步原語。
三、如何寫出執行緒安全的程式碼?
1. 變數
- 區域變數(值型別/參考型別參考)天生安全,因每執行緒擁有獨立堆疊。
- 欄位/屬性若為參考型別,多執行緒可能指向同一物件實例,需同步。
2. 物件實例
- 無共享靜態狀態的實例方法通常安全。
- 若方法操作共享資源(如倉庫庫存),則需同步。
3. 型別
- 靜態建構函式由 CLR 保證執行緒安全(僅執行一次)。
- 靜態方法安全性取決於參數與內部是否存取共享靜態欄位。
四、集合型別是否執行緒安全?
預設皆不安全。 `List`、`Dictionary` 等非執行緒安全。`Hashtable` 提供單寫入者多讀取者安全,但枚舉時仍需鎖定。建議:
- 使用 `Hashtable.Synchronized(new Hashtable())` 取得同步包裝。
- 或自行 `lock(collection.SyncRoot)` 保護枚舉與寫入。
- .NET 4.0+ 推薦使用 `System.Collections.Concurrent` 命名空間下的無鎖集合(`ConcurrentDictionary` 等)。
五、常見同步機制比較
| 機制 | 適用場景 | 備註 |
Interlocked | 整數型變數原子遞增/遞減/交換 | 效能最佳,無鎖 |
lock / Monitor | 一般臨界區保護 | 語法簡潔,`lock` 編譯為 `Monitor.Enter/Exit`+try/finally |
AutoResetEvent / ManualResetEvent | 執行緒間事件通知、順序協調 | 核心物件,開銷較大 |
Mutex | 跨進程同步 | 命名 Mutex 可跨進程,效能較 `Monitor` 差 |
ReaderWriterLockSlim | 讀多寫少的共享資料 | 允許並發讀取,寫入獨佔 |
Semaphore / SemaphoreSlim | 限制同時存取資源的執行緒數(資源池) | `Slim` 版本輕量,適合短等待 |
Barrier | 多階段並行演算法同步點 | .NET 4.0+ |
SpinLock / SpinWait | 極短臨界區、多核低爭用場景 | 用戶模式忙等待,避免核心切換開銷 |
六、IIS 多執行緒架構

- 每個應用程式池對應一個 `w3wp.exe` 進程。
- 進程內含多個應用程式域。
- 應用程式域共享進程級執行緒池(CLR 執行緒池)。
- 某域發生未處理例外可能導致整個進程回收,影響同池其他域。
七、有效使用多執行緒的原則
- 複雜度權衡:僅在收益明顯處引入,避免過度設計。
- 執行緒數量:依 CPU 核心數、I/O 特性測試調整,避免過多造成切換開銷。
- 同步 vs 非同步:I/O 密集型優先用非同步(`async/await`),避免阻塞執行緒池。
- 前景 vs 背景執行緒:長時間監聽、非關鍵任務設為背景(`IsBackground=true`),關鍵業務用前景確保完成。
八、何時使用 ThreadPool?
- 大量短小、獨立的背景工作項目。
- 不需精細控制執行緒屬性(優先級、名稱、前景/背景)。
- 需精確控制(長時間運算、特定優先級、單一實例存取)時改用 `Thread` 或 `Task`。
// ThreadPool 使用範例
ThreadPool.QueueUserWorkItem(state => {
// 背景工作邏輯
});