当方法返回Task包含在try/catch块中的方法时会发生什么?如果那个非等待的Task抛出异常,是否捕获/处理了异常?
例如,如果DoSomethingAsync()抛出异常,我可以在try/catch块中处理它TryCatchMethod()吗?
Task TryCatchMethod()
{
try
{
return DoSomethingAsync();
}
catch(Exception e)
{
//Handle Exception
}
}
async Task DoSomethingAsync()
{
await Task.Delay(10000);
throw new System.Exception();
}
Run Code Online (Sandbox Code Playgroud)
如果DoSomethingAsync抛出异常,则捕获异常.如果它返回一个错误的任务,而不是抛出,那么在你试图获得任务的结果(通过等待它)之前,没有异常可以捕获.由于您不在try块中执行此操作,因此它不会运行您的catch块.
请注意,如果方法被标记为async捕获该方法正文中抛出的任何异常,并导致该方法返回错误任务,而不是抛出异常的方法.对于抛出异常的方法,该方法需要返回a Task而不被标记为async.
不,如果发生以下两种情况,则该try/catch块TryCatchMethod()将不会捕获异常DoSomethingAsync():
MoveNext()DoSomethingAsync()不是awaitd,因此永远不会抛出异常DoSomethingAsync() (首选)async Task TryCatchMethod()
{
try
{
return await DoSomethingAsync();
}
catch(Exception e)
{
//Handle Exception
}
}Run Code Online (Sandbox Code Playgroud)
.GetAwaiter().GetResult()void TryCatchMethod()
{
try
{
DoSomethingAsync().GetAwaiter().GetResult();
}
catch(Exception e)
{
//Handle Exception
}
}Run Code Online (Sandbox Code Playgroud)
注意:.GetAwaiter().GetResult()不建议使用,因为它将锁定当前线程。
要了解为什么该try/catch块未捕获异常,重要的是首先了解编译器如何为async方法生成IL代码。
编译器将一种async方法转换为一个IAsyncStateMachine类,该类允许.NET Runtime“记住”该方法已完成的工作。
该IAsyncStateMachine接口实现MoveNext()中,一种方法执行每次的await运算符是用过的内部async方法。
MoveNext()直到它达到一个基本运行代码await的语句,那么它returns,而在await“d方法执行。这是一种机制,允许当前方法“暂停”,从而将其线程执行交给另一个线程/任务。
MoveNext()仔细观察MoveNext(); 请注意,它包装在一个try/catch块中。
因为编译器创建IAsyncStateMachine的每一个async方法MoveNext()是始终包裹在一个try/catch,一个的每一个抛出的异常的内部async方法一网打尽!
MoveNext现在的问题是,如果每种async方法都捕获了抛出的每个异常,我该如何重新抛出异常?
有几种方法可以重新抛出方法中抛出的异常async:
await关键字(首选)
await DoSomethingAsync().GetAwaiter().GetResult()
DoSomethingAsync().GetAwaiter().GetResult()的await是优选的关键字,因为await允许Task以在不同的线程异步运行,并且它不会锁止当前线程。
.Result还是.Wait()?永不,永不,永不,永不,永不使用.Result或.Wait():
.Result并.Wait()会锁定了当前线程。如果当前线程是主线程(也称为UI线程),则UI将冻结直到Task完成。2. .Result或者.Wait()将您的异常作为抛出System.AggregateException,这使得很难找到实际的异常。不要这样做
var longRunningTask1 = LongRunningTaskAsync();
var longRunningTask2 = LongRunningTaskAsync();
await Task.WhenAll(longRunningTask1, longRunningTask2);
var result1 = longRunningTask1.Result;
var result2 = longRunningTask2.Result;
Run Code Online (Sandbox Code Playgroud)
而是这样做
var longRunningTask1 = LongRunningTaskAsync();
var longRunningTask2 = LongRunningTaskAsync();
await Task.WhenAll(longRunningTask1, longRunningTask2);
var result1 = await longRunningTask1;
var result2 = await longRunningTask2;
Run Code Online (Sandbox Code Playgroud)