| 模式 | 關鍵字/類型 | 狀態 | 特點 |
| APM (Asynchronous Programming Model) | BeginXxx/EndXxx, IAsyncResult | 過時 | 回呼地獄、錯誤處理複雜 |
| EAP (Event-based Asynchronous Pattern) | XxxAsync 事件、XxxCompleted | 過時 | 事件導向、難以組合 |
| TAP (Task-based Asynchronous Pattern) | Task, Task<T>, async/await | 推薦標準 | 可組合、例外處理自然、除錯友善 |
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);
最佳實踐總結
- 預設使用
async/await + Task.Run (TAP)
- I/O 密集型:直接
await 非同步 API(HttpClient、Stream.ReadAsync、EF Core ToListAsync)
- CPU 密集型:
Task.Run 卸載至執行緒池
- 避免
.Result/.Wait() 同步封鎖(死鎖風險)
- 長時間作業支援
CancellationToken
- 平行迴圈用
Parallel.For/ForEach,獨立任務用 Parallel.Invoke
- 例外處理統一用
Task.WhenAll + try/catch
- 上一篇文章: 資料結構基礎:字串定義、ADT 與核心操作應用詳解
- 下一篇文章: Objective-C 屬性修飾符完整解析:atomic/nonatomic、assign/retain/copy 差異與選擇指南
Copyright ©
程式碼之家 All Rights Reserved