任务未处理的异常

时间:2014-02-08 15:49:59

标签: c# exception-handling task

我正在尝试了解在任务对象中抛出但从未处理过的异常会发生什么。

在MSDn上,据说:

  

如果您不等待传播异常或访问的任务   它的异常属性,异常根据提升而升级   任务被垃圾收集时的.NET异常策略。

所以我不太明白这些异常会以何种方式影响程序流程。我认为这些异常应该在垃圾收集后立即中断执行。但我无法设计这种行为。在以下代码段中,抛出的异常不会显示。

 \\Do something ...
 Task.Run (()=> {throw new Exception("Exception in the task!");});
\\Do something else

请有人解释如何处理未处理的任务异常,以及它们如何影响程序流程。

2 个答案:

答案 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;
}

在内部,我们已决定淘汰此扩展方法,因为它太令人困惑了!