42

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

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

有没有人遇到过同样的问题,还是只有我一个人?你有什么解决办法吗?

4

3 回答 3

35

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

当故障任务的未观察到的异常即将触发异常升级策略时发生,默认情况下,该策略将终止进程。

因此,此事件类似于DomainUnhandledException您在问题中提到的事件,但仅针对任务发生。

顺便说一句,未观察到的异常政策(是的,这不是未观察到的异常,MS 家伙又发明了新词……),从 .NET 4.0 更改为 .NET 4.5。在 .NET 4.0 中未观察到的异常会导致进程终止,但在 .NET 4.5 中 - 不要。这都是因为我们将在 C# 5 和 VB 11 中拥有新的异步内容。

于 2012-03-11T11:56:05.923 回答
21

似乎没有内置的方法来处理这个问题(几乎 2 周后没有回答这个问题)。我已经推出了一些自定义代码来解决这个问题。解决方案描述很长,所以我已经在我的博客中发布了。有兴趣可以参考这篇文章。

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();
    }
}
于 2010-05-04T16:56:16.853 回答
12

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

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

任务调度程序有一个 UnobservedTaskException 事件,您可以使用运算符 += 订阅该事件。

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

简介: 这种方法适用于即发即弃的任务以及捕获从集中式异常处理策略中逃逸的异常。

对具有故障状态的任务使用延续。

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

    t.ContinueWith(c => { /* exception handling code */ },
                   TaskContinuationOptions.OnlyOnFaulted);

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

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

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

于 2014-12-02T22:05:54.263 回答