我正在尝试了解在任务对象中抛出但从未处理过的异常会发生什么。
在MSDn上,据说:
如果您不等待传播异常或访问的任务 它的异常属性,异常根据提升而升级 任务被垃圾收集时的.NET异常策略。
所以我不太明白这些异常会以何种方式影响程序流程。我认为这些异常应该在垃圾收集后立即中断执行。但我无法设计这种行为。在以下代码段中,抛出的异常不会显示。
\\Do something ...
Task.Run (()=> {throw new Exception("Exception in the task!");});
\\Do something else
请有人解释如何处理未处理的任务异常,以及它们如何影响程序流程。
答案 0 :(得分:26)
您正在描述.NET 4中的行为,但您很难强制执行垃圾收集并实际观察该行为。 Stephen Toub's excelent write-up关于这个问题的以下引用应该更加明确:
任务会跟踪是否存在未处理的异常 “观察。”在这种情况下,“观察”意味着代码已加入 以某种方式使用Task,以便至少可以了解 例外。这可能是在Task上调用Wait / WaitAll。它 可以在任务之后检查Task的Exception属性 完成。或者它可能正在使用Task的Result属性。 如果任务看到以某种方式观察到其异常, 生活很好。但是,如果删除了对任务的所有引用 (使任务可用于垃圾收集),如果它 异常尚未被观察到,Task知道它的异常 永远不会被观察到。在这种情况下,任务利用了 终结,并使用辅助对象传播未处理的 终结器线程上的异常。随着描述的行为 之前,终结器线程上的异常将无法处理 调用默认的未处理异常逻辑,即记录 问题并使该过程崩溃。
他还提出了两个有用的扩展方法来处理“即发即弃”任务中的异常:一个忽略异常而另一个立即崩溃过程:
public static Task IgnoreExceptions(this Task task)
{
task.ContinueWith(c => { var ignored = c.Exception; },
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.ExecuteSynchronously |
TaskContinuationOptions.DetachedFromParent);
return task;
}
public static Task FailFastOnException(this Task task)
{
task.ContinueWith(c => Environment.FailFast(“Task faulted”, c.Exception),
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.ExecuteSynchronously |
TaskContinuationOptions.DetachedFromParent);
return task;
}
在.NET 4.5 中,默认行为已更改。再次引用another Stephen Toub's post on the subject引用(感谢mike z在评论中引起我的注意):
使开发人员更容易编写基于的异步代码 任务,.NET 4.5更改未观察到的默认异常行为 例外。虽然未观察到的异常仍将导致 要引发的UnobservedTaskException事件(不这样做会是一个 破坏更改),默认情况下进程不会崩溃。相反, 异常将在事件发生后最终被吃掉, 无论事件处理程序是否遵守该异常。这个 但是,可以配置行为。
答案 1 :(得分:1)
请注意,上面的代码不太正确。您必须将指针返回到false
,而不是传递的任务:
!
编辑:
这具有挑战性,因为这取决于您如何将呼叫链接在一起。例如,以下调用无法按我期望的方式工作:
!
此方法确实会引发异常,因为接受的答案将返回初始任务(而不是观察异常的任务)。这可能与其他调用(例如function intenseString(str) {
if (!str.endsWith('!!!')) {
return false;
}
for (let i = 0; i < str.length - 4; i++) {
if (str.charAt(i) === '!' && str.charAt(i + 1) !== ('!')) {
return false;
}
}
return true;
}
,task.ContinueWith
等)有关。人们可能会认为该任务永远不会抛出,但是可以。
但是,我建议的更改将破坏以下内容:
public static Task IgnoreExceptions(this Task task)
{
var t = task.ContinueWith(c => { var ignored = c.Exception; },
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.ExecuteSynchronously);
return t;
}
在内部,我们已决定淘汰此扩展方法,因为它太令人困惑了!