繁体   English   中英

如何处理异步方法引发的混乱异常?

[英]How do I handle the messy exception that is thrown by an async method?

异步方法的异常处理有点奇怪。

如果我写一个同步方法,那就很清楚了。 例如,如果我这样做:

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()

这带来了两个问题:

  1. 伙计,那东西很难看!
  2. 我无法为MyException编写catch块,因为它被包裹在AggregateException中。 换句话说,以下操作无效:

     try { Test2().Wait(); } catch (MyException me) { //This never fires } catch(System.AggregateException e) { //It'll go here instead } 

有标准的处理方法吗? 我有点想让异常更容易阅读,并且我真的很希望能够捕获MyException而不是捕获AggregateException并进行搜索。

链接到演示问题的DotNetFiddle

您有两种选择:

  1. 最好的选择。 始终使用await 如果您使用的是ASP.NET,几乎没有理由在整个堆栈中不使用async / await
  2. 如果您绝对必须在异步方法上同步等待(并且不要轻易做出决定),请使用.GetAwaiter().GetResult() 它将获得与.Result相同的值,但还将解开异常。
  1. 您应该将awaitasynch方法一起使用:它将解压缩异常,使您免于死锁和低效的代码。
  2. 不久前,我使用Ben.Demystifier清理痕迹,这对您的案例也可能有用。

死锁脱离主题: 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);

本文深入讨论了该主题,如果您对asynch / await的陷阱感兴趣,请随时阅读。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM