TaskScheduler.UnobservedTaskException никогда не вызывается
Основываясь на своих исследованиях, я узнал следующее:
TaskScheduler.UnobservedTaskException
необходимо подождать, пока задача будет собрана, прежде чем ненаблюдаемое исключение этой задачи всплывет доUnobservedTaskException
событие.- Если вы используете
Task.Wait()
в любом случае, он никогда не будет вызван, потому что вы блокируете ожидаемый результат от Задачи, следовательно, исключение будет выданоTask.Wait()
а не пузыриться доUnobservedException
событие. - призвание
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" каждый раз перед выключением.