从异步中捕获未处理的exception
当等待的async
方法抛出exception时,exception存储在某处并且抛出它会被延迟。 在WinForms或WPF应用程序中,它使用SynchronizationContext.Current
来发布exception的抛出。 但是,在例如控制台应用程序中,它会在线程池上抛出exception并关闭应用程序。
如何防止async
方法抛出的exception导致应用程序崩溃?
编辑:
显然,我所描述的问题是因为我有void
async
方法。 看评论。
如何防止异步方法抛出的exception导致应用程序崩溃?
遵循以下最佳做法:
- 所有
async
方法都应返回Task
或Task
除非它们必须返回void
(例如,事件处理程序)。 - 在某些时候,您应该
await
从async
方法返回的所有Task
。 您不想这样做的唯一原因是您不再关心操作的结果(例如,在您取消它之后)。 - 如果需要从
async void
事件处理程序中捕获exception,那么在事件处理程序中捕获它 – 就像在同步代码中一样。
您可能会发现我的async
/ await
简介有用; 我还介绍了其他几个最佳实践。
启动async
方法时,它会捕获当前的同步上下文。 解决此问题的方法是创建自己的同步上下文来捕获exception。
这里的要点是同步上下文将回调发布到线程池,但是有一个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
,您可以放置exception处理逻辑。
接下来,在要使用此机制执行async
方法的每个线程( SynchronizationContext.Current
是[ThreadStatic]
)上,必须设置当前同步上下文:
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"); } }