Асинхронные методы, вызываемые из обработчика UnhandledException, не ожидаются

В моем App.xaml.cs я создал обработчик для ответа на AppDomain.CurrentDomain.UnhandledException.

Внутри обработчика мне нужно вызвать асинхронный метод, который отправляет отчет об ошибке по электронной почте. Так что жду метод, а внутри него жду Net.Mail.SmtpClient.SendMailAsync().

Но это не работает — когда элемент управления достигает второго ожидания (прямо перед фактической отправкой), приложение закрывается.

Когда я избавлюсь от всей асинхронности и воспользуюсь синхронной альтернативой, Net.Mail.SmtpClient.Send() электронная почта отправляется правильно.

Кажется, внутри обработчика не выбрасываются никакие другие исключения. Кроме того, похоже, это не просто проблема режима отладки Visual Studio.

Разве нельзя успешно вызывать асинхронные методы внутри обработчика UnhandledException?

Обновление:

Сигнатура моего метода-обработчика выглядит так:

private async void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e);

и я присоединяю обработчик к событию в конструкторе App вот так:

App()
{
    AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}

person zvizesna    schedule 06.12.2019    source источник
comment
Плохое асинхронное поведение можно воспроизвести с помощью простого Task.Delay() прямо в обработчике.   -  person zvizesna    schedule 06.12.2019
comment
Можете ли вы поделиться сигнатурой вашего метода обработчика?   -  person Sidewinder94    schedule 06.12.2019


Ответы (1)


События всегда void. Это означает, что если вы хотите использовать await, вы должны использовать async void. Это вызывает проблему, которую вы видите здесь.

Когда await воздействует на незавершенный Task, остальная часть метода планируется завершить после ожидания, и он возвращает. Обычно он возвращает Task, которого может ожидать вызывающая сторона, но, поскольку возвращаемый тип — void, он ничего не возвращает. Вызывающий не может ждать его, даже если бы захотел. Обычно это не проблема с событиями, поскольку события — это просто уведомление о том, что «что-то» произошло, и то, что вызвало событие, не зависит ни от чего, что происходит внутри обработчика событий.

Но в этом случае фреймворк завершает работу приложения после возврата обработчика событий. Таким образом, поскольку await возвращается, приложение закрывается, не дожидаясь фактического завершения задачи.

Лучшее решение — синхронизировать все в обработчике событий. Это предотвратит его возвращение раньше, чем вы ожидаете.

И под «сохранять синхронность» я имею в виду использование синхронных методов (например, Net.Mail.SmtpClient.Send()). Не ждите синхронно с асинхронными методами, так как это может вызывать взаимоблокировки.

person Gabriel Luci    schedule 06.12.2019
comment
Ой, извините. Вставил синхронную версию обработчика, к которой временно прибегал. Подпись действительно содержала async. - person zvizesna; 06.12.2019
comment
@zvizesna Да, я подумал, что вы уже используете async void. Вот к чему относится мой ответ. Вам нужно удалить async и использовать только синхронные методы внутри вашего обработчика. - person Gabriel Luci; 06.12.2019
comment
Общий совет: никогда не используйте async void, если в этом нет крайней необходимости. Он имеет уникальное поведение по сравнению с любым другим типом асинхронного возврата и, как правило, вызывает подобные сбои, которые не сразу очевидны. - person Captain Prinny; 06.12.2019
comment
@CaptainPrinny Я бы не сказал никогда. Тот факт, что он не полностью запрещен компилятором, означает, что он имеет свое место. Microsoft использует слово избегайте за исключением обработчиков событий (т. е. единственно допустимое использование — в обработчиках событий). Однако есть такие события, когда сразу после завершения обработчика события происходит что-то другое, и async void просто не сработает. - person Gabriel Luci; 06.12.2019
comment
Да, это в основном то, что я повторил. Я имел в виду общий совет => никогда не приближайтесь, если вы точно не знаете, что вам нужно использовать это, не делайте этого. - person Captain Prinny; 06.12.2019