使用任务并行库时如何处理所有未处理的异常?

Buu*_*yen 42 .net multithreading .net-4.0 task-parallel-library unobserved-exception

我在.NET 4.0中使用TPL(任务并行库).我想通过使用Thread.GetDomain().UnhandledException事件集中处理所有未处理异常的处理逻辑.但是,在我的应用程序中,从未使用TPL代码启动的线程触发事件,例如Task.Factory.StartNew(...).如果我使用类似的东西,事件确实会被解雇new Thread(threadStart).Start().

这篇MSDN文章建议使用Task.Wait()来捕获AggregateException使用TPL的时间,但这不是我想要的,因为这种机制不够"集中".

有没有人遇到过同样的问题,还是仅仅是我?你对此有什么解决方案吗?

Ser*_*kov 35

我认为TaskScheduler.UnobservedTaskException事件是你想要的:

在出现故障的Task未观察到的异常即将触发异常升级策略时发生,默认情况下,该策略将终止该进程.

因此,此事件类似于DomainUnhandledException您在问题中提到的事件,但仅适用于任务.

顺便说一句,未观察到的例外政策(是的,这不是一个未被观察到的例外,MS家伙再次发明了新词),从.NET 4.0改为.NET 4.5.在.NET 4.0中,未观察到的异常导致进程终止,但在.NET 4.5中却没有.这都是因为我们将在C#5和VB 11中使用新的异步内容.

  • 问题是只有在完成故障的`Task`对象时才会引发`UnobservedTaskException`事件.并且不能保证对象完成运行. (9认同)

Buu*_*yen 21

似乎没有内置的方法来处理这个问题(并且在将近两周之后没有回答这个问题).我已经推出了一些自定义代码来处理这个问题.解决方案描述相当冗长,所以我发布在我的博客中.如果您有兴趣,请参阅这篇文章.

2010年5月7日更新:我发现了一种更好的方法,可以使用任务延续.我创建了一个class ThreadFactory暴露错误事件的事件,该事件可以由顶级处理程序订阅,并提供启动附加适当延续的任务的方法.
代码发布在这里.

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();
    }
}
Run Code Online (Sandbox Code Playgroud)


Zar*_*trA 10

我看到两个选项可用于在TPL中集中异常处理:1.使用任务调度程序的未观察任务异常事件.2.对故障状态的任务使用延续.

使用任务计划程序的未观察任务异常事件.

任务调度程序具有UnobservedTaskException事件,您可以使用operator + =订阅该事件.

  • 注1:在处理程序主体中,您需要在UnobservedTaskExceptionEventArgs参数上调用SetObserved()以通知调度程序处理了异常.
  • 注2:当垃圾收集器收集任务时调用处理程序.
  • 注3:如果你要等待任务,你仍然会被try/catch块强制保护等待.
  • 注4:.Net 4.0和4.5中未处理的任务异常的默认策略是不同的.

简介:此方法适用于即发即弃任务以及捕获从集中式异常处理策略中转出的异常.

对故障状态的任务使用延续.

使用TPL,您可以使用方法ContinueWith()将操作附加到任务,该方法采用附加操作和继续选项.在任务终止后将调用此操作,并且仅在选项指定的情况下调用.特别是:

    t.ContinueWith(c => { /* exception handling code */ },
                   TaskContinuationOptions.OnlyOnFaulted);
Run Code Online (Sandbox Code Playgroud)

将异常处理代码的继续安装到Task t.此代码仅在由于未处理的异常而终止任务t的情况下运行.

  • 注1:在异常处理代码中获取异常值.否则它将被冒出来.
  • 注2:在任务终止后立即调用异常处理代码.
  • 注3:如果在异常处理代码中得到异常,它将被视为已处理,任务等待的try/catch块将无法捕获它.

我认为集中式异常处理最好使用从Task继承的自定义任务,并通过continuation添加异常处理程序.并通过使用任务计划程序的未观察任务异常事件来捕获此方法,以捕获尝试使用非自定义任务.