Async Await 异常处理

Async 方法有三种可能的返回类型: Task、Task<T> 和 void,但是 async 方法的固有返回类型只有 Task 和 Task<T>。 当从同步转换为异步代码时,任何返回类型 T 的方法都会成为返回 Task<T> 的 async 方法,任何返回 void 的方法都会成为返回 Task 的 async 方法。 返回 void 的 async 方法具有特定用途: 用于支持异步事件处理程序。(参考 https://docs.microsoft.com/zh-cn/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming#%E5%A7%8B%E7%BB%88%E4%BD%BF%E7%94%A8-async, 后面再说asyn void。

Async Task Aync Task<T> 意味着该方法实际上会自动返回对正在进行的操作的引用,然后您可以在其他地方等待它(参考https://app.pluralsight.com/library/courses/getting-started-with-asynchronous-programming-dotnet/transcript)。在这个返回的引用中,包括异常对象,以及我们等的结果和状态。而async void,没有Task对象,并不能返回这些内容。

一、await …async task<T> 的异常流

再强调一次,一般不使用async void,这里只是为了模拟事件处理程序。以上代码的运行结果如下:

异常从 async Task ThrowException() 再次抛出后,在调用函数AwaitException_Event()处再被截获。

这是我们期望的结果。

二、task.wait()的异常流

将代码改为如下,调用ThrowException这个Task时,不使用await,而使用wait(),如下

 

那么便得到这样的结果:

 

这是因为异常在Task内部都保存在AggregateException对象里。

每一个Task都会存一个异常列表。当你await 一个task时,第一个异常会重新抛出来,所以你可以捕获指定的异常类型(比如InvalidOperationException)。但是,当你使用Task.Wait或者Task.Result同步地阻塞Task时,所有的异常都被封装在AggregateException抛出来。

可见 await task时,异常处理会更容易。

 

三、直接调用Task而不用await的异常流

如果在21行不用await…

编译程序,有以下两个提醒:

warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

运行时发现结果不一样了,发现收不到异常了。

异常当然不会凭空消失,但是如果不用await,这个异常就像被“吞噬”一样。这是因为,调用程序没有await,没有得到这个Task,同样无法得到这个Task中的异常,异常无法回到调用代码的上下文环境。

可见,调用async Task一定要使用 await.

四、async void的异常流

前面提到过要避免使用async void (除了异步事件或委托)。现在举个例子,将ThrowException的返回类型改为async void。那么此时编译器也会告诉不能在调用ThrowException的地方使用await。

所以将代码改为如下

运行后,我们得到这个结果,异常没有被“吞噬”,而是直接抛出来了。在AwaitException_Event中的try…catch并不能catch到。这是因为这个方法没有Task,在AwaitException_Event中我们也并不能await,所以异常就在程序当前上下文直接抛出来。因为这是控制台程序,如果在GUI(如WPF和UWP)程序中,在全局的UnHandled_Exception中,我们可以收到这个异常。但是如果有多个这样的async void抛出类似这种异常,我们并不能区分以有效处理。可见使用async void是非常危险的行为。

以上四种情况可得,在async,await中,最佳的异常处理就是按推荐的方式,所以被调用的异步函数中使用async Task或者async Task<T> ,使得调用者可以通过await得到函数的引用,获得期间的结果和异常信息。

可运行示例代码

https://download.csdn.net/download/mochounv/12876928

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值