控制同時執行的執行緒數量是常見需求 (如限制並發下載、資料庫連線、API 呼叫)。早期常用 Thread + 計數器 + lock 手動實作,現代 C# 建議使用 SemaphoreSlim 或 TPL Dataflow 獲得更安全、可組合的方案。
using System;
using System.Threading;
using System.Threading.Tasks;
public class ConcurrencyLimiter
{
private readonly SemaphoreSlim _semaphore;
public ConcurrencyLimiter(int maxConcurrency)
{
_semaphore = new SemaphoreSlim(maxConcurrency, maxConcurrency);
}
// 同步版本
public void Execute(Action action)
{
_semaphore.Wait();
try { action(); }
finally { _semaphore.Release(); }
}
// 非同步版本 (推薦)
public async Task ExecuteAsync(Func asyncAction)
{
await _semaphore.WaitAsync();
try { await asyncAction(); }
finally { _semaphore.Release(); }
}
}
// 使用範例
var limiter = new ConcurrencyLimiter(3); // 最多 3 個並發
var tasks = new List();
for (int i = 0; i < 10; i++)
{
int id = i;
tasks.Add(limiter.ExecuteAsync(async () =>
{
Console.WriteLine($"Task {id} 開始");
await Task.Delay(1000); // 模擬 I/O
Console.WriteLine($"Task {id} 完成");
}));
}
await Task.WhenAll(tasks);
using System.Threading.Tasks.Dataflow;
var block = new ActionBlock(async id =>
{
Console.WriteLine($"Task {id} 開始");
await Task.Delay(1000);
Console.WriteLine($"Task {id} 完成");
}, new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 3, // 限制並發數
// BoundedCapacity = 100 // 可選:輸入緩衝區上限,防止記憶體暴增
});
for (int i = 0; i < 10; i++)
await block.SendAsync(i); // 非同步送入,自動背壓
block.Complete();
await block.Completion;
Parallel.ForEach(items, new ParallelOptions { MaxDegreeOfParallelism = 4 }, item =>
{
// CPU 密集運算
ProcessItem(item);
});
| 場景 | 推薦方案 | 理由 |
|---|---|---|
| 一般非同步 I/O 限流 (HTTP、DB、檔案) | SemaphoreSlim | 輕量、支援同步/非同步、容易嵌入既有程式碼 |
| 高吞吐管線、需背壓控制、批次處理 | TPL Dataflow | 內建緩衝區、支援連結多個 Block 形成管線、自動背壓 |
| CPU 密集平行運算 | Parallel.ForEach | 自動分割工作、工作盜演算法、適合計算密集 |
| 需動態調整並發數 | SemaphoreSlim | 可運行時釋放/等待調整,Dataflow 需重建 Block |
finally 區塊釋放,否則信號量會洩漏導致永久阻塞。_semaphore.Wait() 會阻塞執行緒,UI/ASP.NET 應用請改用 await _semaphore.WaitAsync()。Environment.ProcessorCount * 2 (I/O) 或 ProcessorCount (CPU) 開始調整。