.net-使用任务并行库时如何处理所有未处理的异常?
我在.NET 4.0中使用TPL(任务并行库)。 我想通过使用AggregateException
事件集中所有未处理的异常的处理逻辑。 但是,在我的应用程序中,永远不会为以TPL代码开头的线程触发该事件。 Task.Factory.StartNew(...)
.如果我使用类似new Thread(threadStart).Start()
的事件,则确实会触发该事件。
此MSDN文章建议在使用TPL时使用Task.Wait()来捕获AggregateException
,但这不是我想要的,因为此机制不够“集中”。
有人遇到过同样的问题吗?还是只有我一个人? 您对此有什么解决方案吗?
我认为TaskScheduler.UnobservedTaskException事件是您想要的:
在错误的任务的未观察到的异常即将触发时发生 异常升级策略,默认情况下,该策略将终止 处理。
因此,此事件类似于您在问题中提到的DomainUnhandledException
,但仅针对任务发生。
顺便说一句,未观察到的例外策略(是的,这不是不可观察到的例外,MS专家又发明了新单词……)从.NET 4.0更改为.NET 4.5。 在.NET 4.0中,未观察到的异常导致进程终止,但在.NET 4.5中-不会。 这是因为我们将在C#5和VB 11中使用新的异步内容。
似乎没有内置的方法可以处理此问题(将近两周后也无法回答该问题)。 我已经推出了一些自定义代码来解决这个问题。 解决方案的描述非常冗长,因此我已经在博客中发布了。 如果您有兴趣,请参阅这篇文章。
2010年5月7日更新:我发现了一种更好的方法,可以利用任务继续功能。 我创建了一个class ThreadFactory
,它公开了可以由顶级处理程序订阅的Error事件,并提供了以适当的连续性启动附加任务的方法。
代码在这里发布。
2011年4月18日更新:根据Nifle的评论,来自博客文章的邮政编码。
internal class ThreadFactory
{
public delegate void TaskError(Task task, Exception error);
public static readonly ThreadFactory Instance = new ThreadFactory();
private ThreadFactory() {}
public event TaskError Error;
public void InvokeError(Task task, Exception error)
{
TaskError handler = Error;
if (handler != null) handler(task, error);
}
public void Start(Action action)
{
var task = new Task(action);
Start(task);
}
public void Start(Action action, TaskCreationOptions options)
{
var task = new Task(action, options);
Start(task);
}
private void Start(Task task)
{
task.ContinueWith(t => InvokeError(t, t.Exception.InnerException),
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.ExecuteSynchronously);
task.Start();
}
}
我看到两个选项可用于在TPL中集中处理异常: 1.使用任务计划程序的未观察到的任务异常事件。 2.对状态错误的任务使用延续。
使用任务计划程序的未观察到的任务异常事件。
任务计划程序具有UnobservedTaskException事件,您可以使用运算符+ =订阅该事件。
- 注意1:在处理程序主体中,您需要对UnobservedTaskExceptionEventArgs参数调用SetObserved()以通知调度程序已处理了异常。
- 注2:当任务由垃圾收集器收集时,将调用处理程序。
- 注意3:如果您要等待任务,仍然会被try / catch块强制保护等待。
- 注意4:.Net 4.0和4.5中未处理的任务异常的默认策略不同。
简介:这种方法对于即发即弃任务以及捕获从集中式异常处理策略中逸出的异常非常有用。
对状态错误的任务使用延续。
使用TPL,您可以使用方法ContinueWith()将动作附加到任务,该方法带有附加动作和继续选项。 该操作将在任务终止后且仅在option指定的情况下调用。 特别是:
t.ContinueWith(c => { /* exception handling code */ },
TaskContinuationOptions.OnlyOnFaulted);
将带有异常处理代码的延续安装到任务t。 仅当Task t由于未处理的异常而终止时,此代码才会运行。
- 注1:在异常处理代码中获取异常值。 否则它将冒泡。
- 注2:终止任务后,将立即调用异常处理代码。
- 注意3:如果异常处理代码中包含异常,则将其视为已处理,任务等待中的try / catch块将无法捕获该异常。
我认为集中式异常处理最好使用从Task继承的自定义Task,并通过继续添加异常处理程序。 并通过使用Task Scheduler的Unobserved Task Exception事件来捕获尝试使用未自定义任务的尝试,以此作为辅助。