使用 Async 时如何中断和还原

本篇是 使用 Async 时如何汇报进度及取消任务 的狗尾续貂,演示了使用 async 时如何中断任务和如何在中断(或取消)任务后执行还原任务。

注:下面的用法只是个人的一点经验,如果你有更好的方案,欢迎一起讨论。

中断任务

外部中断通过 CancellationToken,就是之前提到的取消任务,此处不再复述。

内部中断参考 CancellationToken.ThrowIfCancellationRequested,通过抛出 OperationCanceledException:

  1. var ex = new OperationCanceledException($"Interrupt: {reason}");
  2. // 可以通过 Exception 的 Data 属性携带一些和中断任务相关的信息
  3. ex.Data.Add("IsNeedCleanup", isNeedCleanup);
  4. throw ex;

调用者依然通过捕获异常来处理中断:

  1. try
  2. {
  3. int uploads = await UploadPicturesAsync(GenerateTestImages(), progressIndicator, _cts.Token);
  4. }
  5. catch (OperationCanceledException ex)
  6. {
  7. if (ex.Message.Contains("Interrupt:"))
  8. {
  9. isNeedCleanup = (bool)ex.Data["IsNeedCleanup"];
  10. ...
  11. }
  12. else ...
  13. }

还原任务

为了保证异步任务无论是正常执行完毕还是中断(或取消)后还原任务都能执行,我们需要将还原任务放在 finally 块内部。

  1. try
  2. {
  3. int uploads = await UploadPicturesAsync(GenerateTestImages(), progressIndicator, _cts.Token);
  4. }
  5. catch (OperationCanceledException ex)
  6. {
  7. if (ex.Message.Contains("Interrupt:"))
  8. {
  9. isNeedCleanup = (bool)ex.Data["IsNeedCleanup"];
  10. ...
  11. }
  12. else ...
  13. }
  14. finally
  15. {
  16. if (isNeedCleanup)
  17. {
  18. await CleanupAsync();
  19. }
  20. }