Windows Runtime (WinRT) 以非同步為核心設計理念,所有可能超過 50ms 的 API 均為非同步。WinRT 定義了四種標準非同步介面,構成非同步程式設計的基石。本文深入解析 IAsyncAction、IAsyncOperation<TResult>、IAsyncActionWithProgress<TProgress>、IAsyncOperationWithProgress<TResult, TProgress> 的特性、建立方式與進階用法。
| 介面 | 返回值 | 進度回報 | 典型用途 |
|---|---|---|---|
| IAsyncAction | 無 | 無 | 單純非同步動作(如寫入檔案、網路請求) |
| IAsyncOperation<TResult> | 有 | 無 | 帶結果的非同步運算(如計算、查詢) |
| IAsyncActionWithProgress<TProgress> | 無 | 有 | 長時間動作需進度回報(如下載、渲染) |
| IAsyncOperationWithProgress<TResult, TProgress> | 有 | 有 | 帶結果且需進度的複雜操作(如 SyndicationClient) |
所有介面均繼承自 IAsyncInfo,提供 Cancel()、Close()、ErrorCode、Id、Status 等共同成員。
System.Runtime.InteropServices.WindowsRuntime.AsyncInfo 提供靜態工廠方法,將 Task 或委派包裝為 WinRT 標準介面:
// IAsyncAction:無返回、無進度
public IAsyncAction CreateAction() {
return AsyncInfo.Run(token => Task.Run(() => {
token.WaitHandle.WaitOne(3000);
token.ThrowIfCancellationRequested();
}, token));
}
// IAsyncOperation:有返回、無進度
public IAsyncOperation CreateOperation(int x, int y) {
return AsyncInfo.Run(token => Task.Run(() => {
token.WaitHandle.WaitOne(3000);
token.ThrowIfCancellationRequested();
return x * y;
}, token));
}
// IAsyncActionWithProgress:無返回、有進度
public IAsyncActionWithProgress CreateActionWithProgress() {
return AsyncInfo.Run((token, progress) => Task.Run(() => {
for (int p = 0; p <= 100; p++) {
token.WaitHandle.WaitOne(100);
token.ThrowIfCancellationRequested();
progress.Report(p); // 回報進度 0-100
}
}, token));
}
// IAsyncOperationWithProgress:有返回、有進度
public IAsyncOperationWithProgress CreateOperationWithProgress() {
return AsyncInfo.Run((token, progress) => Task.Run(() => {
for (int p = 0; p <= 100; p++) {
token.WaitHandle.WaitOne(100);
token.ThrowIfCancellationRequested();
progress.Report(p);
}
return "完成";
}, token));
}
WinRT 非同步模式採用事件基礎模式(EAP),而非 await:
var action = CreateActionWithProgress();
// 完成事件
action.Completed = (asyncInfo, status) => {
switch (status) {
case AsyncStatus.Completed:
Debug.WriteLine("成功完成");
break;
case AsyncStatus.Canceled:
Debug.WriteLine("已取消");
break;
case AsyncStatus.Error:
Debug.WriteLine($"錯誤: {asyncInfo.ErrorCode}");
break;
}
};
// 進度事件(僅 WithProgress 介面)
action.Progress = (asyncInfo, progressValue) => {
Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
ProgressBar.Value = progressValue;
});
};
// 取消操作
action.Cancel();
雖然 WinRT 原生使用事件模式,但 await 可直接消費這四種介面,編譯器自動處理轉換:
// 直接 await IAsyncAction
await CreateAction();
// await IAsyncOperation 獲取結果
int result = await CreateOperation(10, 20);
// await 帶進度操作(需手動訂閱進度事件)
var op = CreateOperationWithProgress();
op.Progress = (_, p) => UpdateProgress(p);
string result = await op;
注意: await 會捕獲當前 SynchronizationContext,UI 線程上預設回到 UI 線程。使用 .ConfigureAwait(false) 可避免切換回 UI 線程,提升效能。
CancellationToken 串聯 Task 與 WinRT 非同步操作:
var cts = new CancellationTokenSource();
var action = AsyncInfo.Run(token => Task.Run(() => {
for (int i = 0; i < 100; i++) {
token.WaitHandle.WaitOne(100);
token.ThrowIfCancellationRequested(); // 拋出 OperationCanceledException
}
}, token));
// 3 秒後自動取消
cts.CancelAfter(TimeSpan.FromSeconds(3));
try {
await action;
} catch (OperationCanceledException) {
// 處理取消
}
StorageFile.DeleteAsync()、MediaCapture.StartRecordToStorageFileAsync()FileOpenPicker.PickSingleFileAsync()、HttpClient.GetStringAsync()SyndicationClient.RetrieveFeedAsync()、大檔案下載與解析token.ThrowIfCancellationRequested()。IDisposable 的非同步操作用完呼叫 Close()。.GetResults() 或 .Wait(),務必 await。掌握 WinRT 四大非同步介面的建立、消費與互操作,是開發流暢、回應迅速 Windows 8 Store Apps 的核心技能。