Следует ли мне избегать обработчиков событий async void?

119

Я знаю, что обычно считается плохой идеей использовать async voidметоды «запустил и забыл» для запуска задач, потому что не отслеживается ожидающая задача и сложно обрабатывать исключения, которые могут возникнуть внутри такого метода.

Следует ли мне вообще избегать async voidобработчиков событий? Например,

private async void Form_Load(object sender, System.EventArgs e)
{
        await Task.Delay(2000); // do async work
        // ...
} 

Я могу переписать это так:

Task onFormLoadTask = null; // track the task, can implement cancellation

private void Form_Load(object sender, System.EventArgs e)
{
        this.onFormLoadTask = OnFormLoadTaskAsync(sender, e);
} 

private async Task OnFormLoadTaskAsync(object sender, System.EventArgs e)
{
        await Task.Delay(2000); // do async work
        // ...
} 

Каковы подводные камни для обработчиков асинхронных событий, помимо возможного повторного входа?

AVO
источник
Вы должны, но не можете. Кроме того, все заботы, которые вы должны предпринять при использовании async void, уже требуются обработчикам событий пользовательского интерфейса.
Пауло Моргадо
И повторный вход происходит из-за асинхронных операций, запускаемых обработчиком событий, а не за счет использования async-await сам по себе.
Пауло Моргадо

Ответы:

153

Рекомендуется избегать, async void за исключением случаев использования в обработчике событий, поэтому использование async voidв обработчике событий - это нормально.

Тем не менее, по причинам модульного тестирования мне часто нравится исключать логику всех async voidметодов. Например,

public async Task OnFormLoadAsync(object sender, EventArgs e)
{
  await Task.Delay(2000);
  ...
}

private async void Form_Load(object sender, EventArgs e)
{
  await OnFormLoadAsync(sender, e);
}
Стивен Клири
источник
Мне любопытно ... есть ли причина, по которой вы просто не меняете Form_Loadдоступ к public? Похоже, так код был бы менее подробным.
InteXX
Упс, неважно ... VBer пытается прочитать здесь C # ... Я только что заметил возвращаемый тип OnFormLoadAsync. Теперь я понимаю, что это удобный трюк. Спасибо.
InteXX
Все сказанное, не могли бы вы взглянуть и высказать свое мнение здесь . Спасибо!
InteXX
2
@ AlexHopeO'Connor: HandledФлаг должен быть установлен синхронно; его невозможно использовать asyncдля принятия решения о том, обработано событие или нет.
Стивен Клири
2
@ AlexHopeO'Connor: Я давно не работал с приложением WPF, но я использовал решения, похожие на те, что использовались в прошлом. Т.е. сделать ICommand.Executeметод async void; Я считаю это приемлемым , поскольку ICommand.Executeявляется логически обработчик события.
Стивен Клири
50

Следует ли мне вообще избегать обработчиков событий async void?

Как правило, обработчики событий - это единственный случай, когда метод void async не является потенциальным запахом кода.

Теперь, если вам по какой-то причине нужно отслеживать задачу, то описанная вами методика вполне разумна.

Эрик Липперт
источник
6

Да, обычно async без обработчиков событий - единственный случай. Если вы хотите узнать об этом больше, вы можете посмотреть отличное видео здесь, на канале 9.

The only case where this kind of fire-and-forget is appropriate is in top-level event-handlers. Every other async method in your code should return "async Task".

вот ссылка

Идрис Хан
источник
« Обработчики событий верхнего уровня » - важный намек. При использовании обработчика событий async void в обработчике событий нижнего уровня это может вызвать огромные проблемы с неперехваченными исключениями.
Portikus
Спасибо за ссылку на видео, очень полезно
lsp
5

Если вы используете ReSharper, вам может быть полезно бесплатное расширение ReCommended . Он анализирует методы "async void" и выделяет при неправильном использовании. Расширение может различать различные варианты использования async void и предоставлять соответствующие быстрые исправления, описанные здесь: ReCommended-Extension wiki .

Александр Цвитбаум
источник