#c# #asynchronous
#c# #асинхронный
Вопрос:
Вот мой код :
class StateManager
{
private readonly ConcurrentDictionary<int, bool> _states = new ConcurrentDictionary<int, bool>();
public void SetState(int id, bool state)
{
_states[id] = state;
if (!state)
RemoveLately(id);
}
private async void RemoveLately(int id)
{
await Task.Delay(10000).ConfigureAwait(false);
_states.TryRemove(id, out _);
}
}
Моя цель — удалить элемент по истечении определенного промежутка времени. Я не хочу использовать Task.Выполнить для RemoveLately
, поскольку это может быть вызвано тысячи раз.
Каковы могут быть недостатки такой практики, если таковые имеются?
Комментарии:
1. Измените
void
наTask
.2.
async void
предназначен только для обработчиков событий.RemoveLately
не является обработчиком событий. Недостатки велики и хорошо документированы.async void
методы нельзя ожидать или отслеживать. ЕслиStateManager
удален, метод все равно будет запущен и попытается вызвать_states.TryRemove()
Ответ №1:
Моя цель — удалить элемент по истечении определенного промежутка времени.
Тогда почему бы не использовать кеш?
Хорошо ли использовать async void?
Руководство для async void
заключается в том, чтобы избегать async void
, если вы не реализуете обработчик событий (или что-то логически похожее на обработчик событий). Итак, реальный вопрос здесь таков: RemoveLately
логически ли это «обработчик событий»? Я вижу аргумент, в котором его можно было бы считать таковым; в частности, TryRemove
вызывается в ответ на «событие» таймера ( Task.Delay
). Так что я бы не стал абсолютно утверждать, что async void
здесь неправильно, но у него есть недостатки.
Каковы могут быть недостатки такой практики, если таковые имеются?
Существует одна основная проблема с async void
методами: другой код не может знать, когда этот метод завершен.
Эта основная проблема проявляется несколькими способами:
- Ваш код не может перехватывать или обрабатывать исключения из
RemoveLately
. Поскольку нет способа наблюдать завершение дляasync void
методов, также нет способа наблюдать исключения. Таким образом,async void
методы просто вызывают любые исключения непосредственно в их оригиналеSynchronizationContext
. В большинстве случаев это означает, что исключения вasync void
методах приведут к аварийному завершению работы приложения. async void
методы сложно тестировать. Это потому, что код модульного тестирования не может знать, когдаasync void
метод завершен.- Ваш код не может знать, когда безопасно завершить работу (где область действия «завершить работу» может означать «выйти из программы», или «утилизировать
StateManager
«, или что-либо промежуточное). Это потому, что ваш код не может знать, может лиasync void
еще выполняться работа. В данном конкретном случае, когдаRemoveLately
просто удаляется объект из кэша, это должно быть нормально игнорировать, но в общем случаеasync void
означает, что приложение никогда не узнает, когда это «сделано».
Ответ №2:
Отсутствие async void (почти) всегда плохо (так это). Как указано Стивеном в его статье и его ответе здесь, есть несколько причин для использования методов async void (а именно async Eventhandlers, которые никогда не имеют никакого другого «возвращаемого» значения, кроме void). Причины, по которым вам следует перейти на асинхронную задачу, и почему они лучше, объяснены Стивеном в его ответе.
Вот почему я бы предложил, чтобы ваш метод выглядел следующим образом:
private async Task RemoveLately(int id)
{
await Task.Delay(10000).ConfigureAwait(false);
_states.TryRemove(id, out _);
}
В качестве небольшого замечания (если можно): Если вы не уверены, что выбрать, и / или вы можете выбрать либо Task
(или Task<T>
), либо async void
, то попробуйте использовать Task
, потому что почти во всех случаях (кроме обработки событий) вы лучше справляетесь с Task
, чем с async void
.