首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

C# 非同步程式設計與執行緒實戰完全指南(.NET 4.5+)

     編輯:更多程式設計知識

核心概念釐清

同步 vs 非同步

  • 同步:呼叫者等待被呼叫者完成才繼續,循序執行
  • 非同步:呼叫後立即返回,呼叫者可繼續執行其他工作,被呼叫者在背景完成

非同步 ≠ 多執行緒

  • 非同步是程式設計模式,執行緒是執行資源
  • .NET 4.5+ 編譯器/執行環境自動管理執行緒池,開發者多無需手動建立 Thread

.NET 三種非同步模式演進

Task 與執行緒池

  • Task 代表非同步作業,非執行緒本身
  • 執行來源:執行緒池(預設)、新執行緒、同步執行、無執行緒(純計算/已完成)
  • 開發者專注作業邏輯,執行緒管理交給執行環境

手動控制 Thread(少數必要場景)

var thread = new Thread(() => {
    // 背景工作
});
thread.Name = "Worker";
thread.IsBackground = true;  // 不阻礙程式結束
thread.Priority = ThreadPriority.AboveNormal;
thread.Start();

// 傳遞參數
new Thread(obj => {
    var data = (string)obj;
}).Start("parameter");
  • 需設定 Name、Priority、IsBackground 等屬性時使用
  • Lambda 簡化委派語法

async/await 核心語法

private async static void CallerWithAsync() {
    string result = await GetSomethingAsync();
    Console.WriteLine(result); // 等待完成後續執行
}

// 簽名限制:回傳 Task、Task 或 void(僅限事件處理器)
private async Task GetSomethingAsync() { ... }
  • await 僅能用於 async 標記方法
  • 可等待型別:Task、Task<T>、自訂實作 INotifyCompletion
  • await 暫停當前方法,釋放執行緒,完成後續接執行

Task 建立與組合

建立 Task


// .NET 4.5+ 推薦
Task t = Task.Run(() => Greeting(name));

// 已有結果直接包裝
Task t = Task.FromResult(42);

等待單一 Task

string result = await GreetingAsync("Bob");

續作


Task t1 = GreetingAsync("Bob");
t1.ContinueWith(t => Console.WriteLine(t.Result));

並行等待多個 Task


// 循序等待(較慢)
var r1 = await GreetingAsync("A");
var r2 = await GreetingAsync("B");

// 並行等待(推薦)
Task t1 = GreetingAsync("A");
Task t2 = GreetingAsync("B");
await Task.WhenAll(t1, t2);
Console.WriteLine($"{t1.Result}, {t2.Result}");

// 任一完成
await Task.WhenAny(t1, t2);

例外處理策略

循序 await:僅捕獲第一個例外


try {
    await Task1();
    await Task2(); // Task1 失敗則不執行
} catch (Exception ex) { ... }

Task.WhenAll:聚合所有例外


try {
    await Task.WhenAll(t1, t2);
} catch (Exception ex) {
    // ex 為 AggregateException
    foreach (var inner in ex.InnerExceptions) { ... }
}

進階:保留 Task 參考檢查 IsFaulted


Task taskResult = null;
try {
    Task t1 = ..., t2 = ...;
    await (taskResult = Task.WhenAll(t1, t2));
} catch {
    if (taskResult.IsFaulted) {
        foreach (var inner in taskResult.Exception.InnerExceptions) { ... }
    }
}

取消機制


var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(1));

try {
    var result = await GreetingAsync("Bob", cts.Token);
} catch (OperationCanceledException) {
    // 處理取消
}

// 長時間作業中定期檢查
static string Greeting(string name, CancellationToken token) {
    Thread.Sleep(3000);
    token.ThrowIfCancellationRequested();
    return $"Hello, {name}";
}

平行程式設計

資料平行


Parallel.For(0, 100, async (i, state) => {
    Console.WriteLine($"{i}, Task: {Task.CurrentId}, Thread: {Thread.CurrentThread.ManagedThreadId}");
    await Task.Delay(10);
    if (i > 5) state.Break();
});

任務平行


Parallel.Invoke(MethodOne, MethodTwo, MethodThree);

最佳實踐總結

  1. 預設使用 async/await + Task.Run (TAP)
  2. I/O 密集型:直接 await 非同步 API(HttpClient、Stream.ReadAsync、EF Core ToListAsync)
  3. CPU 密集型:Task.Run 卸載至執行緒池
  4. 避免 .Result/.Wait() 同步封鎖(死鎖風險)
  5. 長時間作業支援 CancellationToken
  6. 平行迴圈用 Parallel.For/ForEach,獨立任務用 Parallel.Invoke
  7. 例外處理統一用 Task.WhenAll + try/catch
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
模式關鍵字/類型狀態特點
APM (Asynchronous Programming Model)BeginXxx/EndXxx, IAsyncResult過時回呼地獄、錯誤處理複雜
EAP (Event-based Asynchronous Pattern)XxxAsync 事件、XxxCompleted過時事件導向、難以組合
TAP (Task-based Asynchronous Pattern)Task, Task<T>, async/await推薦標準可組合、例外處理自然、除錯友善