Исключение (исключения) задачи не наблюдалось ни при ожидании задачи, ни при доступе к ее свойству Exception. В результате необнаруженное исключение было
Что это значит и как его решить?
Я использую задачи TPL.
Вся ошибка
Исключение (исключения) задачи не наблюдалось ни при ожидании задачи, ни при доступе к ее свойству Exception. В результате незаметное исключение было восстановлено потоком финализатора.
в System.Threading.Tasks.TaskExceptionHolder.Finalize()
mscorlib
Ответы
Ответ 1
Если вы создаете задачу, и вы никогда не вызываете task.Wait()
или не пытаетесь получить результат Task<T>
, когда сбор сборщика мусора собирается, он будет разорвать ваше приложение во время финализации. Подробнее см. на странице MSDN в Обработка исключений в TPL.
Лучший вариант здесь - "обработать" исключение. Это можно сделать с помощью продолжения - вы можете приложить продолжение к задаче и выполнить log/swallow/etc исключение. Это обеспечивает чистый способ регистрации исключений задач и может быть записан как простой метод расширения, то есть:
public static void LogExceptions(this Task task)
{
task.ContinueWith( t =>
{
var aggException = t.Exception.Flatten();
foreach(var exception in aggException.InnerExceptions)
LogException(exception);
},
TaskContinuationOptions.OnlyOnFaulted);
}
С помощью вышеизложенного вы можете не допустить, чтобы любая задача отрывала приложение и регистрировала его, используя:
Task.Factory.StartNew( () =>
{
// Do your work...
}).LogExceptions();
Кроме того, вы можете подписаться на TaskScheduler.UnobservedTaskException и обрабатывать его там.
Ответ 2
Конечно; это означает, что Task
получил финализацию после того, как его оставили на сборку мусора, но сама задача потерпела неудачу. Есть два исправления:
- обрабатывать задачи сбой напрямую (используйте
ContinueWith(...)
для подписки и проверьте .IsFaulted
и .Exception
на Task
в параметре)
- обрабатывать событие
TaskScheduler.UnobservedTaskException
и отмечать его наблюдаемым (вызов e.SetObserved()
после регистрации ошибки)
Ответ 3
Попробуйте следующее:
public static void ThrowFirstExceptionIfHappens(this Task task)
{
task.ContinueWith(t =>
{
var aggException = t.Exception.Flatten();
foreach (var exception in aggException.InnerExceptions)
{
throw exception; // throw only first, search for solution
}
},
TaskContinuationOptions.OnlyOnFaulted); // not valid for multi task continuations
}
public static Task CreateHandledTask(Action action)
{
Task tsk = Task.Factory.StartNew(action);
tsk.ThrowFirstExceptionIfHappens();
return tsk;
}