TaskScheduler.UnobservedTaskException никогда не вызывается - PullRequest
13 голосов
/ 20 октября 2010

Основываясь на своих исследованиях, я узнал следующее:

  1. TaskScheduler.UnobservedTaskException должен дождаться, пока задача будет собрана сборщиком мусора, прежде чем ненаблюдаемое исключение этой задачи будет всплыть до события UnobservedTaskException.
  2. Если вы используете Task.Wait(), он все равно никогда не будет вызван, потому что вы блокируете ожидаемый результат от Задачи, следовательно, исключение будет выдано Task.Wait(), а не всплытьна событие UnobservedException.
  3. Вызов GC.Collect() вручную, как правило, плохая идея, если вы точно не знаете, что делаете, поэтому в данном случае это хорошо для подтверждения вещей, но не как правильное решение дляпроблема.

Проблема

Если мое приложение завершит работу до того, как начнет работать сборщик мусора, я абсолютно на 100% не смогу запустить событие UnobservedTaskException.

Обратите внимание на следующий код:

class Program
{
    static void Main(string[] args)
    {
        TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;

        Task.Factory.StartNew(() =>
        {
            Console.WriteLine("Task started.");
            throw new Exception("Test Exception");
        });

        Thread.Sleep(1000);
        //GC.Collect();
        //GC.WaitForPendingFinalizers();
    }

    static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
    {
        File.WriteAllText(@"C:\data\TestException.txt", e.Exception.ToString());
        Console.WriteLine("UNOBSERVED EXCEPTION");
    }
}

Файл исключений не записывается и ничего не записывается в консоль.После выхода из приложения может пройти 10-15 минут и более, и все же я не вижу доказательств того, что останки моего приложения были собраны мусором.Вы можете спросить, ну почему бы просто не собрать при выходе?Что ж, мой реальный сценарий таков, что моя ловушка исключений запускается внутри службы WCF, размещенной внутри службы Windows.Я не могу перехватить, когда служба Windows завершает работу (и, следовательно, вручную вызывает GC.Collect()), потому что, насколько я вижу, для этого нет события.

Куда я иду не так?Как я могу гарантировать, что если что-то глубоко внутри службы WCF в конечном итоге сломает мою службу Windows, у меня будет шанс зарегистрировать исключение до того, как служба перестанет работать?

Ответы [ 2 ]

8 голосов
/ 22 октября 2012

Для меня TaskScheduler.UnobservedTaskException вначале дает очень неправильное чувство безопасности.Это на самом деле не стоит многого, если это зависит от сборки мусора.

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

Возможно, вы также захотите сгладить вложенные исключения AggregateExceptions и, возможно, создать метод расширения,как Рид Копси, изображенный здесь .

var task1 = Task.Factory.StartNew(() =>
{
    throw new MyCustomException("Task1 faulted.");
})
.ContinueWith((t) =>
{
    Console.WriteLine("I have observed a {0}",
        t.Exception.InnerException.GetType().Name);
},
TaskContinuationOptions.OnlyOnFaulted);
2 голосов
/ 20 октября 2010

Натан,

Все ваши очки верны.Попробуйте сделать следующее:

namespace ConsoleApplication1
{
    using System;
    using System.Threading;
    using System.Threading.Tasks;

    class Program
    {
        static void Main(string[] args)
        {
            TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;

            Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Task started.");
                throw new Exception("Test Exception");
            });

            Thread.Sleep(1000);
            Console.WriteLine("First Collect");
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Console.WriteLine("Waiting");
            Console.ReadKey();
        }

        static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
        {
            Console.WriteLine("UNOBSERVED EXCEPTION");
        }
    }
}

Я заметил, что отладчик часто «перехватывает» событие UnobservedTaskException, вызывая его неправильную работу.Запустите этот вне отладчика , и он будет печатать «UNOBSERVED EXCEPTION» каждый раз перед выключением.

...