在任务中处理exception的正确方法将继续

请看下面的代码 –

static void Main(string[] args) { // Get the task. var task = Task.Factory.StartNew<int>(() => { return div(32, 0); }); // For error handling. task.ContinueWith(t => { Console.WriteLine(t.Exception.Message); }, TaskContinuationOptions.OnlyOnFaulted); // If it succeeded. task.ContinueWith(t => { Console.WriteLine(t.Result); }, TaskContinuationOptions.OnlyOnRanToCompletion); Console.ReadKey(); Console.WriteLine("Hello"); } private static int div(int x, int y) { if (y == 0) { throw new ArgumentException("y"); } return x / y; } 

如果我在释放模式下执行代码,输出是“发生了一个或多个错误”,一旦我点击“Enter”键,“Hello”也会显示出来,如果我在debugging模式下运行代码,但在IDE中进行debugging时,控件执行该行时出现IDEexception消息(“用户代码中未处理的exception”)

 throw new ArgumentException("y"); 

如果我从那里继续,程序不会崩溃,并显示与释放模式相同的输出。 这是正确的方式来处理exception?

您可能不需要单独的OnlyOnFaultedOnlyOnRanToCompletion处理程序,并且您不处理OnlyOnCanceled 。 检查这个答案的更多细节。

但在IDE中进行debugging时,控件执行该行时会出现IDEexception消息(“用户代码中未处理的exception”)

您在debugging器下看到exception,因为您可能已经在debugging/例外选项( Ctrl + Alt + E )中启用了它。

如果我从那里继续,程序不会崩溃,并显示与释放模式相同的输出。 这是正确的方式来处理exception?

抛出的exception在Task动作中不会被自动重新抛出。 相反,将它作为Task.Exception (types为AggregateException )包装以供将来观察。 您可以以Exception.InnerException方式访问原始Exception.InnerException

 Exception ex = task.Exception; if (ex != null && ex.InnerException != null) ex = ex.InnerException; 

在这种情况下,为了使程序崩溃,实际上需要观察任务操作之外的exception,例如通过引用Task.Result

 static void Main(string[] args) { // Get the task. var task = Task.Factory.StartNew<int>(() => { return div(32, 0); }); // For error handling. task.ContinueWith(t => { Console.WriteLine(t.Exception.Message); }, TaskContinuationOptions.OnlyOnFaulted); // If it succeeded. task.ContinueWith(t => { Console.WriteLine(t.Result); }, TaskContinuationOptions.OnlyOnRanToCompletion); Console.ReadKey(); Console.WriteLine("result: " + task.Result); // will crash here // you can also check task.Exception Console.WriteLine("Hello"); } 

更多详细信息: 任务和未处理的exception , .NET 4.5中的任务exception处理

更新以解决评论:这是我将如何在.NET 4.0和VS2010 UI应用程序中执行此操作:

 void Button_Click(object sender, EventArgs e) { Task.Factory.StartNew<int>(() => { return div(32, 0); }).ContinueWith((t) => { if (t.IsFaulted) { // faulted with exception Exception ex = t.Exception; while (ex is AggregateException && ex.InnerException != null) ex = ex.InnerException; MessageBox.Show("Error: " + ex.Message); } else if (t.IsCanceled) { // this should not happen // as you don't pass a CancellationToken into your task MessageBox.Show("Canclled."); } else { // completed successfully MessageBox.Show("Result: " + t.Result); } }, TaskScheduler.FromCurrentSynchronizationContext()); } 

只要您的目标是.NET 4.0,并且您希望.NET 4.0行为用于不可观察的exception(例如,在任务被垃圾收集时重新抛出),则应该在app.config 显式configuration它:

 <?xml version="1.0" encoding="utf-8"?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> </startup> <runtime> <ThrowUnobservedTaskExceptions enabled="true"/> </runtime> </configuration> 

检查这个更多细节:

.NET4中未观察到的任务exception

你在那里有一个AggregateException 。 这是从任务中抛出的,并要求您检查内部exception以查找特定的exception。 喜欢这个:

 task.ContinueWith(t => { if (t.Exception is AggregateException) // is it an AggregateException? { var ae = t.Exception as AggregateException; foreach (var e in ae.InnerExceptions) // loop them and print their messages { Console.WriteLine(e.Message); // output is "y" .. because that's what you threw } } }, TaskContinuationOptions.OnlyOnFaulted); 

发生“一个或多个错误”来自任务池所产生的包装exception。 如果需要,使用Console.WriteLine(t.Exception.ToString())打印整个exception。

无论是否处理,IDE都可以自动捕获所有exception。

由于您正在使用任务,您应该得到AggregateException ,它包装执行期间发生的所有exception。 您会看到One or more errors occurred消息,因为它是AggregateException.ToString()方法的默认输出。

您需要处理exception实例的方法。

另请参阅此处处理此类exception的正确方法。

  try { var t1 = Task.Delay(1000); var t2 = t1.ContinueWith(t => { Console.WriteLine("task 2"); throw new Exception("task 2 error"); }, TaskContinuationOptions.OnlyOnRanToCompletion); var t3 = t2.ContinueWith(_ => { Console.WriteLine("task 3"); return Task.Delay(1000); }, TaskContinuationOptions.OnlyOnRanToCompletion).Unwrap(); // The key is to await for all tasks rather than just // the first or last task. await Task.WhenAll(t1, t2, t3); } catch (AggregateException aex) { aex.Flatten().Handle(ex => { // handle your exceptions here Console.WriteLine(ex.Message); return true; }); }