异步方法的异常处理有点奇怪。
如果我写一个同步方法,那很清楚。例如,如果我这样做:
public static void Test1()
{
ThrowException("Test1 has a problem");
}
public static void ThrowException(string message)
{
throw new MyException(message.ToUpper());
}
我得到一个相对容易阅读的异常:
MyException thrown. Message: TEST1 HAS A PROBLEM
Trace: at Program.ThrowException(String message)
at Program.Test1()
at Program.Main()
但是,如果我做的事情非常相似,但却是异步的:
public static async Task Test2()
{
await ThrowExceptionAsync("Test2 has a problem");
}
public static async Task ThrowExceptionAsync(string message)
{
throw new MyException(message.ToUpper());
}
我一团糟:
System.AggregateException thrown. Message: One or more errors occurred.
Trace: at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Task.Wait()
at Program.Main()
MyException thrown. Message: TEST2 HAS A PROBLEM
Trace: at Program.<ThrowExceptionAsync>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Program.<Test2>d__1.MoveNext()
MyException thrown. Message: TEST2 HAS A PROBLEM
Trace: at Program.<ThrowExceptionAsync>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Program.<Test2>d__1.MoveNext()
这带来了两个问题:
我无法为catch
编写一个MyException
块,因为它被包裹在AggregateException中。换句话说,以下操作无效:
try
{
Test2().Wait();
}
catch (MyException me)
{
//This never fires
}
catch(System.AggregateException e)
{
//It'll go here instead
}
是否有处理此问题的标准方法?我想让该异常更容易阅读,并且我真的很希望能够捕获MyException
而不是捕获AggregateException
并对其进行搜索。
答案 0 :(得分:3)
您有两个选择:
await
。如果您使用的是ASP.NET,几乎没有理由在整个堆栈中不使用async
/ await
。.GetAwaiter().GetResult()
。它将获得与.Result
相同的值,但也会解开异常。答案 1 :(得分:3)
await
与asynch
方法一起使用:它将解压缩异常,使您免于死锁和低效率的代码。死锁脱离主题:async
方法捕获当前的同步上下文,因此,如果尝试在同一同步上下文中等待完成,则会出现死锁。
public partial class Form1 : Form
{
public Form1() => InitializeComponent();
private void button1_Click(object sender, EventArgs e)
=> doSomthing().Wait();
private async Task doSomthing() => await Task.Delay(1000);
}
要不捕获上下文,可以使用ConfigureAwait
:
private void button1_Click(object sender, EventArgs e)
=> doSomthing()
.ConfigureAwait(continueOnCapturedContext: false)
.GetAwaiter()
.GetResult();
private async Task doSomthing() => await Task.Delay(1000);
This article深入讨论了此主题,如果您对asynch
/ await
的陷阱感兴趣,请随时阅读。