Async Await 异常处理

Async 方法有三种可能的返回类型: Task、Task 和 void,但是 async 方法的固有返回类型只有 Task 和 Task。 当从同步转换为异步代码时,任何返回类型 T 的方法都会成为返回 Task 的 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 意味着该方法实际上会自动返回对正在进行的操作的引用,然后您可以在其他地方等待它(参考https://app.pluralsight.com/library/courses/getting-started-with-asynchronous-programming-dotnet/transcript)。在这个返回的引用中,包括异常对象,以及我们等的结果和状态。而async void,没有Task对象,并不能返回这些内容。

一、await …async task 的异常流

Async Await 异常处理_第1张图片

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

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

这是我们期望的结果。

二、task.wait()的异常流

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

 

Async Await 异常处理_第2张图片

那么便得到这样的结果:

 

这是因为异常在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。

所以将代码改为如下

Async Await 异常处理_第3张图片

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

Async Await 异常处理_第4张图片

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

可运行示例代码

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

你可能感兴趣的:(c#,多线程学习笔记,Async,Await,异常,async,void,异常,Task.wait异常,CS4014,CS1998)