2025年6月1日 星期日

C# 非同步背景任務完整攻略:async/await、ThreadPool、SemaphoreSlim 與協程並發控制(含範例)

在現代 C# 開發中,如何有效執行背景任務、限制併發數量、與 UI 協作已成為必備技能。本篇文章整理各種方式的特性、應用場景,並附上實用程式碼範例。

🧩 為何需要多種背景任務執行方式?

背景任務可避免 UI 卡頓、提升效能並允許多工操作。常見需求包括:

  • 執行不阻塞主線程(UI)
  • 控制同時併發的數量
  • 可 await 等待完成
  • 與 WinForms / WPF UI 結合

✅ 背景任務方式比較(含執行類型)

技術控制併發數支援 async/await適用場景UI 支援執行方式
BackgroundWorker舊式 WinForms/WPF 背景任務Thread
ThreadPool快速提交背景任務Thread
Task / Task.Run一般非同步任務Thread
Task + SemaphoreSlim控制併發數、IO 密集任務Thread + Coroutine
Parallel.ForEachCPU 密集型處理Thread
TPL Dataflow複雜資料管線與佇列Thread
async/await✅(手動限制)非同步 API / DB / IO 流程Coroutine

🧪 範例程式碼

1️⃣ BackgroundWorker

var worker = new BackgroundWorker();
worker.DoWork += (s, e) =>
{
    Thread.Sleep(2000);
    e.Result = "任務完成";
};
worker.RunWorkerCompleted += (s, e) =>
{
    MessageBox.Show((string)e.Result);
};
worker.RunWorkerAsync();

2️⃣ ThreadPool

ThreadPool.QueueUserWorkItem(state =>
{
    Console.WriteLine("背景任務開始");
    Thread.Sleep(1000);
    Console.WriteLine("任務完成");
});

3️⃣ Task / Task.Run


await Task.Run(() =>
{
    Thread.Sleep(1000);
    Console.WriteLine("Task 執行完成");
});

4️⃣ Task + SemaphoreSlim(控制併發數)

var semaphore = new SemaphoreSlim(3);
var tasks = new List<Task>();

for (int i = 0; i < 10; i++)
{
    int index = i;
    tasks.Add(Task.Run(async () =>
    {
        await semaphore.WaitAsync();
        try
        {
            Console.WriteLine($"工作 {index} 開始");
            await Task.Delay(2000);
            Console.WriteLine($"工作 {index} 結束");
        }
        finally
        {
            semaphore.Release();
        }
    }));
}

await Task.WhenAll(tasks);

5️⃣ Parallel.ForEach

Parallel.ForEach(Enumerable.Range(0, 5), i =>
{
    Console.WriteLine($"執行任務 {i} - 線程 {Thread.CurrentThread.ManagedThreadId}");
    Thread.Sleep(1000);
});

6️⃣ TPL Dataflow

var block = new ActionBlock<int>(async i =>
{
    Console.WriteLine($"處理 {i}");
    await Task.Delay(500);
}, new ExecutionDataflowBlockOptions
{
    MaxDegreeOfParallelism = 3
});

foreach (var i in Enumerable.Range(0, 10))
{
    block.Post(i);
}

block.Complete();
await block.Completion;

📦 需安裝: System.Threading.Tasks.Dataflow

7️⃣ async/await Coroutine

async Task<string> DownloadAsync(string url)
{
    using var client = new HttpClient();
    var result = await client.GetStringAsync(url);
    return result;
}

var html = await DownloadAsync("https://example.com");
Console.WriteLine(html);

🧠 Thread vs Coroutine

類型說明
Thread作業系統層級,適合同步或 CPU 密集任務
Coroutine由 .NET 控制流程(Task + await),讓出執行緒等 IO

💡 小結

  • Taskasync/await 是現代開發主力
  • SemaphoreSlim 控制併發數
  • ⚠️ BackgroundWorker 僅建議用於舊 UI 專案

沒有留言: