TaskScheduler.UnobservedTaskException никогда не вызывается

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

  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 ответа

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

Я нашел следующее решение, взятое из этой статьи, гораздо более надежным. В основном он выполняет блок продолжения (в котором вы регистрируете исключение) только в том случае, если в 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);

Натан,

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

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" каждый раз перед выключением.

Другие вопросы по тегам