2012-09-07 88 views
18

当等待的async方法抛出异常时,该异常存储在某个地方并抛出它被延迟。在WinForms或WPF应用程序中,它使用SynchronizationContext.Current来引发抛出异常。但是,例如,一个控制台应用程序,它会在线程池中引发异常,并使应用程序停止运行。从异步中捕获未处理的异常

如何防止从async方法抛出的异常将应用程序关闭?

编辑:

Appearantly我所描述的问题是因为我有voidasync方法。看评论。

+2

如果等待异步方法,则在等待它的代码中引发异常。只有当方法返回“void”时,未处理的异常才会这样。这是尽可能避免使用“异步无效”方法的原因。 – svick

+1

@svick'async void'方法导致确定性行为;调用一个导致出现异常并且不等待任务的“任务”返回函数将会在垃圾收集器运行时在将来的某个半任意点引发'UnobservedTaskException'事件,如果这种情况什么都不做,那么让程序静静地继续如果一切都好的话。问题不在于'async void'方法,它们只是暴露了真正的问题。如果您不*从'async'方法调用'Task'返回函数,那么您很可能会做错某些事情。 – hvd

+0

顺便说一下,只有“很好的机会”,因为有一些例外情况(无双关语意图)的情况下,可以丢弃例外,但不是很多。 – hvd

回答

10

async方法启动时,它捕获当前的同步上下文。解决此问题的一种方法是创建您自己的同步上下文来捕获异常。

的这里的一点是,同步上下文的帖子回调线程池,但它周围的try/catch语句:

public class AsyncSynchronizationContext : SynchronizationContext 
{ 
    public override void Send(SendOrPostCallback d, object state) 
    { 
     try 
     { 
      d(state); 
     } 
     catch (Exception ex) 
     { 
      // Put your exception handling logic here. 

      Console.WriteLine(ex.Message); 
     } 
    } 

    public override void Post(SendOrPostCallback d, object state) 
    { 
     try 
     { 
      d(state); 
     } 
     catch (Exception ex) 
     { 
      // Put your exception handling logic here. 

      Console.WriteLine(ex.Message); 
     } 
    } 
} 

catch上面你可以把你的异常处理逻辑。

接下来,每一个线程(SynchronizationContext.Current[ThreadStatic])要执行async方法用这种机制,则必须设置当前同步上下文:

SynchronizationContext.SetSynchronizationContext(new AsyncSynchronizationContext()); 

完整Main例如:

class Program 
{ 
    static void Main(string[] args) 
    { 
     SynchronizationContext.SetSynchronizationContext(new AsyncSynchronizationContext()); 

     ExecuteAsyncMethod(); 

     Console.ReadKey(); 
    } 

    private static async void ExecuteAsyncMethod() 
    { 
     await AsyncMethod(); 
    } 

    private static async Task AsyncMethod() 
    { 
     throw new Exception("Exception from async"); 
    } 
} 
+3

我很抱歉,但是我没有看到你的SynchronizationContext在线程池中发布了什么,只要我能看到它所做的就是立即在调用线程上执行函数并返回。 –

18

如何防止从异步方法抛出的异常关闭应用程序?

遵循以下最佳实践:

  1. 所有async方法应该返回TaskTask<T>,除非他们返回void(例如,事件处理程序)。
  2. 在某些时候,你应该从async方法await所有Task s返回。如果您不再关心操作的结果(例如,在取消操作后),您不希望这样做的唯一原因。
  3. 如果你需要从一个async void事件处理程序中捕获一个异常,然后在事件处理程序中捕获它 - 完全像你会这样做,如果这是同步代码。

您可能会发现我的async/await intro post有帮助;我还介绍了其他几个最佳实践。

+1

我知道我得到的问题是我有'无效''async'方法。但是,这是否意味着我所描述的问题确实发生在事件处理程序上? –

+0

是的;重申第三点,如果你有一个'async void'事件处理程序,你可能想要在*那个事件处理程序中捕获异常*。同步事件处理程序会有完全相同的问题 - 如果异常在线程池线程上运行时发生异常,它会使应用程序崩溃。 –

+0

除了异常被路由到'async'方法被调用时捕获的'SynchronizationContext'。在WinForms/WPF应用程序中,大多数时候这将是一个正确的同步上下文,因为事件很可能是从UI线程启动的。看起来行为与事件不是“异步”时发生的行为没有什么不同。 –