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

时间:2010-04-25 05:40:13

标签: .net multithreading .net-4.0 task-parallel-library unobserved-exception

我在.NET 4.0中使用TPL(Task Parallel Library)。我想通过使用Thread.GetDomain().UnhandledException事件来集中处理所有未处理异常的处理逻辑。但是,在我的应用程序中,从未使用TPL代码启动的线程触发事件,例如, Task.Factory.StartNew(...)。如果我使用类似new Thread(threadStart).Start()的内容,事件确实会被触发。

This MSDN article建议在使用TPL时使用Task.Wait()来捕获AggregateException,但这不是我想要的,因为这种机制不够“集中”。

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

3 个答案:

答案 0 :(得分:33)

我认为TaskScheduler.UnobservedTaskException Event就是你想要的:

  

在故障Task's未观察到的异常即将触发时发生   异常升级策略,默认情况下会终止   过程

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

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

答案 1 :(得分:21)

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

2010年5月7日更新:我发现了一种更好的方法,可以继续使用任务。我创建了一个class ThreadFactory来公开Error事件,该事件可以由顶级处理程序订阅,并提供启动任务的方法以及正确的继续。
代码已发布here

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();
    }
}

答案 2 :(得分: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);

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

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

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