当方法返回Task
块中包含的try/catch
时会发生什么?如果那个未等待的Task
抛出异常,是否会捕获/处理异常?
例如,如果DoSomethingAsync()
引发异常,我可以在try/catch
的{{1}}块中处理吗?
TryCatchMethod()
答案 0 :(得分:6)
如果DoSomethingAsync
抛出异常,则会捕获异常。如果它返回一个错误的任务,而不是抛出,那么在你试图获得任务的结果(通过等待它)之前,没有异常可以捕获。由于您未在try
块中执行此操作,因此不会运行catch
块。
请注意,如果方法标记为async
,则会捕获该方法正文中抛出的任何异常,并导致该方法返回错误的任务,而不是抛出异常的方法。对于抛出异常的方法,该方法需要返回Task
而不被标记为async
。
答案 1 :(得分:5)
不,try/catch
中的TryCatchMethod()
阻止因{2}原因DoSomethingAsync()
中出现异常而无法捕获异常:
MoveNext()
DoSomethingAsync()
不是await
d,因此永远不会重新抛出异常DoSomethingAsync()
(首选) async Task TryCatchMethod()
{
try
{
return await DoSomethingAsync();
}
catch(Exception e)
{
//Handle Exception
}
}
.GetAwaiter().GetResult()
void TryCatchMethod()
{
try
{
DoSomethingAsync().GetAwaiter().GetResult();
}
catch(Exception e)
{
//Handle Exception
}
}
注意:建议不要使用.GetAwaiter().GetResult()
,因为它会锁定当前线程。
要理解try/catch
块没有捕获异常的原因,首先要了解编译器如何为async
方法生成IL代码非常重要。
(资料来源:Xamarin University: Using Async and Await)
编译器将async
方法转换为IAsyncStateMachine
类,允许.NET运行时“记住”方法已完成的内容。
(资料来源:Xamarin University: Using Async and Await)
IAsyncStateMachine
接口实现MoveNext()
,这是一种在await
方法中每次使用async
运算符时执行的方法。
MoveNext()
基本上运行代码,直到它达到await
语句,然后return
执行await
方法时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()
和Task
都将锁定当前线程。如果当前线程是主线程(也称为UI线程),则UI将冻结,直到.Result
完成。
2. .Wait()
或System.AggregateException
将您的例外重新抛出为var longRunningTask1 = LongRunningTaskAsync();
var longRunningTask2 = LongRunningTaskAsync();
await Task.WhenAll(longRunningTask1, longRunningTask2);
var result1 = longRunningTask1.Result;
var result2 = longRunningTask2.Result;
,这使得很难找到实际的例外。不要这样做
var longRunningTask1 = LongRunningTaskAsync();
var longRunningTask2 = LongRunningTaskAsync();
await Task.WhenAll(longRunningTask1, longRunningTask2);
var result1 = await longRunningTask1;
var result2 = await longRunningTask2;
相反,请执行此操作
--jars
答案 2 :(得分:1)
等待任务时抛出异常。当等待关键字等待任务时,编译器生成代码巧妙地解开异常并抛出它们对合理合理的堆栈跟踪的期望。如果您访问任务的结果或调用等待,但它被抛出作为聚合异常,这也是抛出,这不是最好的做法。如果您从未等待任务,则异常最终会成为未经执行的任务异常,并且对您的程序可能是致命的,并且还有垃圾堆栈跟踪
答案 3 :(得分:0)
使用await task
,task.Result
或task Wait()
访问任务执行结果时,将抛出异常。
在您的情况下,如果在DoSomethingAsync()
内的任务内引发异常,它将不会被TryCatchMethod()
捕获,但会被等待{{1返回的任务的外部方法捕获}}