为什么我不能从异步代码中捕获exception?

我读到它的任何地方都说下面的代码应该可行,但事实并非如此。

public async Task DoSomething(int x) { try { // Asynchronous implementation. await Task.Run(() => { throw new Exception(); x++; }); } catch (Exception ex) { // Handle exceptions ? } } 

也就是说,我没有抓到任何东西,并且在“投掷”线上得到一个“未处理的例外”。 我在这里很无能为力。

您已打开“仅我的代码”选项。 有了这个,它正在考虑关于“只是你的代码”未处理的exception – 因为其他代码正在捕获exception并将其填充到Task中,稍后将在await调用中重新抛出并被catch语句捕获。

如果没有附加在调试器中,您的catch语句将被触发,它将按预期运行。 或者您可以从调试器中继续运行,它将按预期运行。

最好的办法就是关闭“只是我的代码”。 国际海事组织,它导致更多的混乱而不是它的价值。

正如SLaks所说,你的代码运行良好。

我强烈怀疑你过度简化了你的例子,并且你的代码中有一个async void

以下工作正常

 private static void Main(string[] args) { CallAsync(); Console.Read(); } public static async void CallAsync() { try { await DoSomething(); } catch (Exception) { // Handle exceptions ? Console.WriteLine("In the catch"); } } public static Task DoSomething() { return Task.Run(() => { throw new Exception(); }); } 

以下不起作用

 private static void Main(string[] args) { CallAsync(); Console.Read(); } public static void CallAsync() { try { DoSomething(); } catch (Exception) { // Handle exceptions ? Console.WriteLine("In the catch"); } } public static async void DoSomething() { await Task.Run(() => { throw new Exception(); }); } 

请参阅http://msdn.microsoft.com/en-us/magazine/jj991977.aspx

异步void方法具有不同的error handling语义。 当异步任务或异步任务方法抛出exception时,将捕获该exception并将其放在Task对象上。 使用async void方法,没有Task对象,因此异步void方法抛出的任何exception都将直接在异步void方法启动时处于活动状态的SynchronizationContext上引发。 图2说明了异步void方法抛出的exception无法自然捕获。

你的代码目前甚至不会像x++;一样干净利落地编译x++; 声明无法访问。 始终注意警告。

但是,在修复之后,它工作正常:

 using System; using System.Threading.Tasks; class Test { static void Main(string[] args) { DoSomething(10).Wait(); } public static async Task DoSomething(int x) { try { // Asynchronous implementation. await Task.Run(() => { throw new Exception("Bang!"); }); } catch (Exception ex) { Console.WriteLine("I caught an exception! {0}", ex.Message); } } } 

输出:

 I caught an exception! Bang! 

(请注意,如果您在WinForms应用程序中尝试上述代码,您将遇到死锁,因为您正在等待需要返回UI线程的任务。我们可以在控制台应用程序中作为任务将在线程池线程上恢复。)

我怀疑问题实际上只是调试问题 – 调试器可能认为它未处理,即使它已被处理。

而不是使用await ,访问Task.Result属性并trycatch该访问。 您也可以按照此处的示例尝试该样式。

请记住,在任务线程的上下文中抛出的所有exception都包含在AggregateException

例外不是真的。 原因是 – 当执行下面的语句时

 await Task.Run(() => { throw new Exception("Bang!"); }); 

它在一个单独的线程上。 在该线程上引发的exception没有被捕获。

将其更改为如下所示

 await Task.Run(() => { try { throw new Exception("Bang!"); } catch (Exception ex) { Console.WriteLine(ex.Message); } });