请查看以下代码 -
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”也会显示。如果我在调试模式下运行代码,输出与发布模式相同。但在IDE中调试时,控件执行行时会出现IDE异常消息(“用户代码中未处理的异常”)
throw new ArgumentException("y");
如果我从那里继续,程序不会崩溃并显示与发布模式相同的输出。这是处理异常的正确方法吗?
答案 0 :(得分:63)
您可能不需要单独的OnlyOnFaulted
和OnlyOnRanToCompletion
处理程序,并且您没有处理OnlyOnCanceled
。查看this answer了解更多详情。
但是在IDE中调试时,会出现一条IDE异常消息(“未处理 当控件执行行
时,会出现用户代码“)中的异常
您在调试器下看到异常,因为您可能已在Debug / Exceptions选项中启用它( Ctrl + Alt + E )。< / p>
如果我从那里继续,程序不会崩溃并显示 与发布模式相同的输出。这是正确的处理方式吗? 异常?
在Task
操作中抛出但未处理的异常不会自动重新抛出。相反,它将被包装以供将来观察Task.Exception
(类型AggregateException
)。您可以访问原始例外Exception.InnerException
:
Exception ex = task.Exception;
if (ex != null && ex.InnerException != null)
ex = ex.InnerException;
要在这种情况下使程序崩溃,您实际上需要观察异常外部任务操作,例如通过引用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");
}
更多详情:Tasks and Unhandled Exceptions,Task Exception Handling in .NET 4.5 。
更新以解决评论:以下是我在使用.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行为用于未观察到的异常(即,当任务被垃圾收集时重新抛出),您应该显式配置它app.config
:
<?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>
检查以获取更多详细信息:
答案 1 :(得分:4)
你所拥有的是AggregateException
。这是从任务中抛出的,需要您检查内部异常以查找特定的异常。像这样:
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);
答案 2 :(得分:3)
从.Net 4.5开始,您可以使用.col
返回“此异常的根本原因”。
虽然文档似乎有点过时了。它声称返回另一个AggregateException。但是我认为你会发现它返回抛出的ArgumentException。
答案 3 :(得分:0)
“发生一个或多个错误”来自任务池生成的包装器异常。如果需要,请使用Console.WriteLine(t.Exception.ToString())
打印整个例外。
IDE可以自动捕获所有异常,无论它们是否被处理。
答案 4 :(得分:0)
由于您正在使用任务,因此应该获得AggregateException,其中包含执行期间发生的所有异常。您会看到One or more errors occurred
消息,因为它是AggregateException.ToString()
方法的默认输出。
您需要异常实例的Handle方法。
另请参阅处理此类异常的正确方法here。
答案 5 :(得分:-5)
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;
});
}