Async 方法有三种可能的返回类型: Task、Task
Async Task或 Aync Task
一、await …async task
再强调一次,一般不使用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
可运行示例代码
https://download.csdn.net/download/mochounv/12876928