异步编程时经常需要向用户显示任务的进度并提供取消任务的功能,本篇就讲解在使用 async 时如何做到这两点。
本篇基本就是 Async in 4.5: Enabling Progress and Cancellation in Async APIs 的一个摘录,英语好的话还是推荐阅读原博文。
.NET 中主要有 3 种异步编程模式,对于新代码最推荐的模式就是基于 Task 的 Task-based asynchronous pattern (TAP) ,关于 TAP 模式的详细内容请参考 Docs。
注:Docs 中把 TAP 模式的实现(Implementing)和使用(Consuming)的示例放到了两个文档中。
汇报进度
通过实现 IProgress
Async 方法内部使用 Report 方法汇报进度:
public async Task<int> UploadPicturesAsync(List<Image> imageList,IProgress<int> progress){int totalCount = imageList.Count;int processCount = await Task.Run<int>(() =>{int tempCount = 0;foreach (var image in imageList){//await the processing and uploading logic hereint processed = await UploadAndProcessAsync(image);if (progress != null){progress.Report((tempCount * 100 / totalCount));}tempCount++;}return tempCount;});return processCount;}
根据 Report 的进度刷新前台的进度条控件:
private async void Start_Button_Click(object sender, RoutedEventArgs e){int uploads = await UploadPicturesAsync(GenerateTestImages(),new Progress<int>(percent => progressBar1.Value = percent));}
取消任务
取消任务用的是 CancellationToken 结构。
CancellationToken 结构:异步方法如果想支持取消,就在异步方法的签名中增加一个 CancellationToken 参数,然后调用者调用异步方法时传入一个 CancellationToken,即调用者和异步方法共享一个 CancellationToken。
最常见的流程是:
- 调用者创建一个 CancellationTokenSource 对象
- 调用者调用支持取消的异步方法,并传入 CancellationTokenSource.Token
- 调用者使用 CancellationTokenSource.Cancel() 来取消异步方法
- 异步方法内确认取消并取消任务,通常使用 CancellationToken.ThrowIfCancellationRequested
修改方法签名以传入 CancellationToken,然后在异步方法中通过 ct.ThrowIfCancellationRequested() 检查任务是否已被取消,若已取消则抛出 OperationCanceledException。
public async Task<int> UploadPicturesAsync(List<Image> imageList,IProgress<int> progress,CancellationToken ct){int processCount = await Task.Run<int>(() =>{foreach (var image in imageList){//await UploadAndProcessAsync (this is another method in the app)bool success = await UploadAndProcessAsync(image);ct.ThrowIfCancellationRequested();// progress logic here}},ct);return processCount;}
ThrowIfCancellationRequested() 源码:
public void ThrowIfCancellationRequested(){if (!this.IsCancellationRequested)return;this.ThrowOperationCanceledException();}
注:.NET 内置的耗时较长的异步方法基本都有支持 CancellationToken 的重载。如 await Task.Delay(2000, ct) 。
- 调用方法时传入 CancellationTokenSource 的 Token
- 通过调用 Cancel 方法触发取消请求
- 通过捕获 OperationCanceledException 处理任务的取消 ```csharp private CancellationTokenSource _cts;
private async void Start_Button_Click(object sender, RoutedEventArgs e)
{
var progressIndicator = new Progress
private void Cancel_Button_Click(object sender, RoutedEventArgs e) { _cts.Cancel(); } ```

