做飯過程天然包含非同步操作:
public class Cooking {
public void StartCooking() {
BoilingWater(); // 10 秒
AddRice(); // 10 秒
}
private void BoilingWater() { /* Thread.Sleep(10000) */ }
private void AddRice() { /* Thread.Sleep(10000) */ }
}
public class Frying {
public void Fry() { /* Thread.Sleep(10000) */ }
}
public delegate void AsyncDel(); // 無參無回傳委派
public class Person {
private Cooking cooking = new Cooking();
private Frying frying = new Frying();
public void StartWork() {
// 非同步啟動做飯
AsyncDel ad1 = new AsyncDel(cooking.StartCooking);
IAsyncResult ar1 = ad1.BeginInvoke(new AsyncCallback(CallbackMethod), ad1);
// 非同步啟動炒菜
AsyncDel ad2 = new AsyncDel(frying.Fry);
IAsyncResult ar2 = ad2.BeginInvoke(null, null);
// 等待兩者完成(阻塞當前執行緒)
while (!ar1.IsCompleted) { /* 可做其他事 */ }
while (!ar2.IsCompleted) { /* 可做其他事 */ }
Console.WriteLine("開飯了.......");
}
// 回呼方法:結束非同步呼叫
public void CallbackMethod(IAsyncResult ar) {
AsyncDel ad = (AsyncDel)ar.AsyncState;
ad.EndInvoke(ar); // 必須呼叫 EndInvoke 釋放資源、取得回傳值/例外
}
}
| 概念 | 對應代碼 | 說明 |
|---|---|---|
| 委派 | AsyncDel | 方法簽章的型別安全封裝 |
| 非同步啟動 | BeginInvoke | 立即返回 IAsyncResult,不阻塞 |
| 非同步狀態 | IAsyncResult.IsCompleted | 輪詢或等待完成 |
| 回呼 | AsyncCallback | 完成時自動執行,執行緒池執行緒上下文 |
| 結束非同步 | EndInvoke | 必須呼叫,否則資源洩漏、例外吞沒 |
主執行緒直接跑到 Console.WriteLine("開飯了") 並結束程式,非同步任務還在背景執行緒跑,導致:
結論:非同步呼叫需明確等待完成(IsCompleted、AsyncWaitHandle、EndInvoke 阻塞等)。
// Task-based Asynchronous Pattern (TAP) - 推薦
public async Task StartWorkAsync() {
var cookTask = Task.Run(() => cooking.StartCooking());
var fryTask = Task.Run(() => frying.Fry());
await Task.WhenAll(cookTask, fryTask);
Console.WriteLine("開飯了.......");
}
async/await + Task 取代委派非同步模式(APM),更簡潔、例外處理更自然、支援取消權杖、進度回報。
委派非同步(BeginInvoke/EndInvoke)是理解非同步本質的基石。掌握「啟動→等待/回呼→結束」三階段,再升級到 Task/async/await,才能寫出高效、健壯的並行程式。