我正在使用.NET 4.0构建Windows服务。
我在Tasks中抛出了各种未处理的异常,但它们并没有像MSDN文档所述那样终止我的进程(Parallel Tasks - 请参阅未观察到的任务异常)。
“如果你没有给出有故障的任务,那就有机会传播它 异常(例如,通过调用Wait方法),运行时将 根据当前情况升级任务的未观察到的异常 任务被垃圾收集时的.NET异常策略。“
即使我使用最简单的任务调用,它的行为也是如此:
Task.Factory.StartNew(() => { throw new Exception(); }
当调用该服务时,该服务保持正常运行。
根据文档,任务的终结器将在任务为GC后重新抛出异常,但这似乎不会发生。 MSDN反复声明,正常的“.NET异常策略”会导致进程终止。
为什么不终止我的应用?我能想到的唯一的事情是某种方式对某个地方的任务的引用(是lambda ??)
答案 0 :(得分:10)
从Essential C# 4.0,第715页,以下内容可能会帮助您:
将禁止执行任务期间的未处理异常 直到调用其中一个任务完成成员:
Wait()
,Result,Task.WaitAll()
或Task.WaitAny()
。这些成员中的每一个都会 抛出任务中发生的任何未处理的异常 执行。
可以使用多种处理异常的方法。有a look at MSDN here。
在回答您的评论时,同一本书中的另一个引用解释了为什么某些例外未传播:
虽然比较少见,但一般规则的例外情况之一 (起泡)碰巧在任务上。 [..]任何基于任务的例外 在应用程序退出期间从终结队列中抛出将进行 抑制。行为是这样设置的,因为经常是effor 处理这样的例外太复杂了[...]
为了克服这个问题,优雅地执行此操作的一种方法是创建异常处理程序任务,并在任务运行后使用ContinueWith
进行跟进。然后,即使在应用程序退出期间在终结队列中抛出异常,您也可以使用parentTask.IsFaulted
并正常崩溃。
提示:使用标志OnlyOnFaulted
仅在发生异常时才运行此任务。
答案 1 :(得分:6)
.NET 4.5对how UnobservedExceptions are handled
进行了一些更改虽然未观察到的异常仍将导致 要引发的UnobservedTaskException事件(不这样做会是一个 破坏更改),默认情况下进程不会崩溃。
但是可以配置此行为,因此您可以通过启用ThrowUnobservedTaskExceptions
来恢复.Net 4.0行为:
<configuration>
<runtime>
<ThrowUnobservedTaskExceptions enabled="true"/>
</runtime>
</configuration>
建议库开发人员在测试时启用此功能,以确保它们不会抛出任何UnobservedExceptions。否则,启用此设置的库消费者可能会看到他们的程序崩溃。
答案 2 :(得分:5)
根据@Hans和@CodeInChaos的建议,我发现重新抛出未处理的异常(从而杀死进程)的唯一方法是强制终结器运行(注意:确保你不要这样做在ContinueWith()
中!):
GC.Collect();
GC.WaitForPendingFinalizers();
在我的特定情况下,任务不是垃圾收集,因为程序的流程取决于任务是否成功。如果没有流程继续,我的应用程序将不会执行任何操作来导致GC(分配对象等)。
有趣的是,即使进行GC.Collect()
也是不够的。任务终结器仍然没有运行。必须明确调用GC.WaitForPendingFinalizers()
。 (我怀疑我不理解Finalization的细微之处)。
总结:不要指望TPL任务的未观察到的异常行为与其他线程机制未处理的异常行为(例如QueueUserWorkItem
)类似。在大多数实际情况中,您需要明确检查“任务中的异常”:您不能依赖于使用QUWI或类似方式引起注意的未观察到的异常,因为您只会看到它们从Finalizer中抛出,这是完全不可预测的。
编辑:请参阅我关于.NET 4.5的其他答案
答案 3 :(得分:0)
您可以使用TaskCreationOptions.AttachedToParent创建它。根据{{3}},异常会传播到您的主题。但是,我不知道这是否优雅。
在大多数情况下,Microsoft不建议这样做。其他人可能知道在什么情况下这可能是明智的。来自同一篇文章:
您可以使用附加的子任务来创建紧密同步 异步操作的图表。但是,在大多数情况下,我们 建议您使用嵌套任务因为与之关系 其他任务不太复杂。这就是在其他内部创建任务的原因 默认情况下嵌套任务,您必须明确指定 AttachedToParent选项用于创建子任务。
干杯,马蒂亚斯
答案 4 :(得分:0)
根据这个不错的blog post,如果您想在任务中出现未处理的异常时立即崩溃您的应用,那么您可以继续执行以下任务:
public static Task FailFastOnException(this Task task)
{
task.ContinueWith(c => Environment.FailFast(“Task faulted”, c.Exception),
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.ExecuteSynchronously |
TaskContinuationOptions.DetachedFromParent);
return task;
}