Как обрабатывать все необработанные исключения при использовании Task Parallel Library? - PullRequest
41 голосов
/ 25 апреля 2010

Я использую TPL ( Task Parallel Library ) в .NET 4.0. Я хочу централизовать логику обработки всех необработанных исключений с помощью события Thread.GetDomain().UnhandledException. Однако в моем приложении событие никогда не запускается для потоков, запущенных с кодом TPL, например, Task.Factory.StartNew(...). Событие действительно запускается, если я использую что-то вроде new Thread(threadStart).Start().

В этой статье MSDN предлагается использовать Task.Wait () для перехвата AggregateException при работе с TPL, но это не то, что мне нужно, поскольку этот механизм недостаточно "централизован".

Кто-нибудь вообще испытывает такую ​​же проблему или это только у меня? Есть ли у вас решение для этого?

Ответы [ 3 ]

34 голосов
/ 11 марта 2012

Я думаю TaskScheduler.UnobservedTaskException Событие - это то, что вы хотите:

Происходит, когда собирается сработать незамеченное исключение Задачи. политика эскалации исключений, которая по умолчанию прервала бы процесс.

Итак, это событие похоже на DomainUnhandledException, которое вы упомянули в своем вопросе, но происходит только для задач.

Кстати, обратите внимание, что политика ненаблюдаемых исключений (да, это не ненаблюдаемые исключения, ребята из MS изобрели новое слово ... снова) изменилась с .NET 4.0 на .NET 4.5 В .NET 4.0 ненаблюдаемое исключение приводит к завершению процесса, а в .NET 4.5 - нет. Это все потому, что у нас будут новые асинхронные компоненты в C # 5 и VB 11.

21 голосов
/ 04 мая 2010

Похоже, что нет встроенного способа справиться с этим (и нет ответа на этот вопрос после почти 2 недель). Я уже выкатил некоторый пользовательский код, чтобы позаботиться об этом. Описание решения довольно длинное, поэтому я разместил в своем блоге. Если вам интересно, обратитесь к этому сообщению .

Обновление от 5.07.2010: Я нашел лучший способ сделать это, используя продолжение задачи. Я создаю class ThreadFactory, который отображает событие Error, которое может быть подписано обработчиком верхнего уровня, и предоставляет методы для запуска задачи с соответствующим продолжением. Код выложен здесь .

Обновление от 18.04.2011: Почтовый индекс из сообщения в блоге согласно комментарию 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();
    }
}
10 голосов
/ 03 декабря 2014

Я вижу две опции, которые можно использовать для целей централизации обработки исключений в TPL: 1. Использование события ненаблюдаемого исключения задачи из планировщика задач. 2. Использование продолжений для задач с ошибочным состоянием.

Использование события исключения ненаблюдаемой задачи в планировщике задач.

В планировщике задач есть событие UnobservedTaskException, на которое вы можете подписаться, используя оператор + =.

  • Примечание 1. В теле обработчика необходимо выполнить вызов SetObserved () для аргумента UnobservedTaskExceptionEventArgs, чтобы уведомить планировщик об обработке исключения.
  • Примечание 2: Обработчик вызывается, когда задачи собраны сборщиком мусора.
  • Примечание 3: Если вы будете ждать задания, вы все равно будете вынуждены защищать ожидание блоком try / catch.
  • Примечание 4: Политика по умолчанию для необработанных исключений задач в .Net 4.0 и 4.5 отличается.

Резюме: Этот подход хорош для задач запуска и забывания и для отлова исключений, вышедших из вашей политики централизованной обработки исключений.

Использование продолжений для задач с ошибочным состоянием.

С TPL вы можете прикрепить действия к Задаче, используя метод ContinueWith (), который принимает присоединение действия и опцию продолжения. Это действие будет вызвано после завершения задачи и только в случаях, указанных опцией. В частности:

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

устанавливает продолжение с кодом обработки исключений в задачу t. Этот код будет работать только в том случае, если задача t была прервана из-за необработанного исключения.

  • Примечание 1: Получить значение исключения в коде обработки исключений. В противном случае это будет пузыриться.
  • Примечание 2: Код обработки исключений будет вызываться сразу после завершения задачи.
  • Примечание 3: Если исключение было получено в коде обработки исключений, оно будет считаться обработанным, блок try / catch при ожидании задачи не сможет его перехватить.

Я думаю, что для централизованной обработки исключений будет лучше использовать пользовательские Задачи, унаследованные от Задачи, с обработчиком исключений, добавленным через продолжение. И сопровождайте этот подход, используя событие «Незаметное исключение задач» в Планировщике задач, чтобы перехватывать попытки использовать ненастроенные задачи.

...