Исключение было сгенерировано до того, как было выполнено ожидание, что оно будет выполняться синхронно
Думал, что это довольно верно, но это не значит, что вы можете перехватить исключение.
Поскольку в вашем коде есть ключевое слово async
, которое превращает метод в асинхронный конечный автомат c, т. Е. Инкапсулированный / обернутый специальным типом. Любое исключение, генерируемое из конечного автомата asyn c, будет перехвачено и переброшено, когда задача await
ed (кроме тех, async void
) или они go ненаблюдаемые, что может быть зафиксировано в событии TaskScheduler.UnobservedTaskException
.
Если вы удалите ключевое слово async
из метода NonAwaitedMethod
, вы можете перехватить исключение.
Хороший способ наблюдать за этим поведением - использовать следующее:
try
{
NonAwaitedMethod();
// You will still see this message in your console despite exception
// being thrown from the above method synchronously, because the method
// has been encapsulated into an async state machine by compiler.
Console.WriteLine("Method Called");
}
catch (Exception e)
{
Console.WriteLine("Exception Caught");
}
Итак, ваш код скомпилирован аналогично следующему:
try
{
var stateMachine = new AsyncStateMachine(() =>
{
try
{
NonAwaitedMethod();
}
catch (Exception ex)
{
stateMachine.Exception = ex;
}
});
// This does not throw exception
stateMachine.Run();
}
catch (Exception e)
{
Console.WriteLine("Exception Caught");
}
, почему переключение с Task на void с возвращаемым типом приводит к тому, что исключение будет поймано
Если Метод возвращает Task
, исключение перехватывается задачей.
Если метод void
, то исключение повторно генерируется из потока произвольного пула потоков. Любое необработанное исключение, выброшенное из потока пула потоков, приведет к тому, что приложение обработает sh, так что скорее всего отладчик (или, возможно, отладчик JIT) наблюдает за такого рода исключениями.
Если вы хотите запустить и забыть но правильно обработав исключение, вы можете использовать ContinueWith
для создания продолжения задачи:
NonAwaitedMethod()
.ContinueWith(task => task.Exception, TaskContinuationOptions.OnlyOnFaulted);
Обратите внимание, что вам нужно посетить свойство task.Exception
, чтобы сделать наблюдаемое исключение, в противном случае планировщик задач все равно получит событие UnobservedTaskException
.
Или, если исключение необходимо перехватить и обработать в Main
, правильный способ сделать это - использовать asyn c Основные методы .