为什么我不能从异步代码中捕获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
属性并try
并catch
该访问。 您也可以按照此处的示例尝试该样式。
请记住,在任务线程的上下文中抛出的所有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); } });