// 簡單火燒忘
ThreadPool.QueueUserWorkItem(state =>
{
DoWork(state);
}, payload);
// 等待可等待控制代碼觸發
ThreadPool.RegisterWaitForSingleObject(
waitHandle, // ManualResetEvent, AutoResetEvent 等
(state, timedOut) => Callback(state),
state,
timeoutMs,
executeOnlyOnce: true);
| 特性 | 說明 |
|---|---|
| 自動調節 | 根據 CPU 核心數、工作負載動態增減工作者線程(預設每核心最多 25 線程,可調 SetMaxThreads/SetMinThreads)。 |
| 背景線程 | 池線程皆為背景線程,不阻礙程序退出。 |
| 無法取消 | 一旦佇列,無法撤回;需自行實作取消令牌模式。 |
| 堆疊大小固定 | 預設 1MB,不可調;深度遞迴謹慎。 |
| 安全性檢查 | 預設流動呼叫堆疊進行 CAS 檢查;高效能場景可用 UnsafeQueueUserWorkItem 跳過(需極高信任)。 |
// 建構:回調、狀態、首次延遲、週期間隔(毫秒)
var timer = new Timer(
callback: state => DoPeriodicWork(),
state: null,
dueTime: TimeSpan.FromSeconds(5), // 5 秒後首次執行
period: TimeSpan.FromMinutes(10)); // 之後每 10 分鐘執行
// 動態調整
timer.Change(TimeSpan.FromSeconds(10), TimeSpan.FromMinutes(5));
// 釋放資源(必須調用,否則回調持續觸發)
timer.Dispose();
System.Windows.Forms.Timer 需 UI 訊息泵)。BeginInvoke/EndInvoke、IAsyncResult。難以組合、錯誤處理分散、回調地獄。XxxAsync + XxxCompleted 事件。適合 UI 繫結,但同樣難組合。Task/Task<T> + async/await。現代標準,支援組合、異常冒泡、取消、進度報告、同步上下文捕獲。同步阻塞:線程等待 I/O 完成 → 線程閒置浪費。
異步非阻塞:發起 I/O → 線程歸還池處理其他請求 → I/O 完成由完成埠通知 → 線程池線程接續後續邏輯。
同樣線程數可服務更多並發請求,關鍵指標:吞吐量、延遲、資源利用率。
// I/O 密集型:使用 async/await,不要 Task.Run 包裝同步方法
public async Task DownloadAsync(string url, CancellationToken ct = default)
{
using var client = new HttpClient();
return await client.GetStringAsync(url, ct);
}
// CPU 密集型:確需卸載至背景線程才用 Task.Run
public Task ComputeHeavyAsync(int[] data) => Task.Run(() => HeavyCompute(data));
// 並行控制:SemaphoreSlim 限制併發度
var semaphore = new SemaphoreSlim(10);
await semaphore.WaitAsync();
try { await DoLimitedWork(); }
finally { semaphore.Release(); }
| 場景 | 推薦方式 | 理由 |
|---|---|---|
| 短時 I/O 任務 | async/await + 內建異步 API | 無線程佔用,最高擴展性 |
| 短時 CPU 任務 | Task.Run | 利用線程池,自動負載平衡 |
| 長時運行/獨占資源 | 自建 Thread (IsBackground=true) | 不污染線程池,可精確控制 |
| 週期性任務 | Timer / PeriodicTimer (.NET 6+) | 高精度、低開銷 |
| 需 STA/線程親和性 | 自建 Thread + SetApartmentState | 線程池皆為 MTA |
.Result/.Wait() 導致死鎖 (UI/ASP.NET 同步上下文) 或線程池飢餓。全鏈路 async/await。for (int i=0; i<10; i++) Task.Run(() => Use(i)) → 全部見到最終值 10。修正:int local = i; 在迴圈內部。Task.Run 未觀察 Task.Exception。使用 await 或 ContinueWith(t => Log(t.Exception), TaskContinuationOptions.OnlyOnFaulted)。線程池與 Timer 是 .NET 並行基礎設施的核心組件,配合現代 async/await (TAP) 模式,能構建高擴展、響應迅速的應用程式。核心原則:I/O 用異步,CPU 用 Task.Run,長任務自建線程,週期用 Timer,永遠觀察異常,謹慎同步等待。