Соображения о том, что не следует ожидать Задачу в асинхронном методе
Я работаю над проектом веб-API, который использует службу управляемого кэширования Azure для кэширования результатов базы данных в памяти, чтобы сократить время отклика и уменьшить дублирующийся трафик в базу данных. При попытке поместить новый элемент в кеш иногда возникает специфичное для кеша исключение с кодом DataCacheErrorCode.RetryLater
, Естественно, чтобы повторить попытку позже, не блокируя этот метод, я сделал это async
а также await Task.Delay
повторить попытку через некоторое время. Ранее разработчик жестко закодировал Thread.Sleep
там это действительно ухудшало производительность приложений.
Подпись метода теперь выглядит примерно так:
public static async Task Put(string cacheKey, object obj)
После этого изменения я получаю ~75 предупреждений компилятора из всех других мест приложения, которое называлось ранее синхронной версией Put
с указанием:
Поскольку этот вызов не ожидается, выполнение текущего метода продолжается до завершения вызова. Попробуйте применить оператор 'await' к результату вызова.
В этом случае, так как Put
ничего не возвращает, имеет смысл разрешить эту операцию запускать и забывать, так как я не вижу причин блокировать выполнение вызывающего ее метода. Мне просто интересно, есть ли какие-нибудь опасности или подводные камни для того, чтобы позволить многим из этих пожаров забыть Task
работает в фоновом режиме, как Put
можно назвать довольно часто. Или я все равно должен ждать, так как в 99% случаев я не получу ошибку повтора и Task
закончит почти сразу. Я просто хочу убедиться, что я не несу никаких штрафов за слишком большое количество потоков (или что-то в этом роде).
4 ответа
Если есть шанс Put
бросит любое другое исключение по любой причине, и вы не используете await Put
каждый раз, когда вы вставляете объект в кеш, исключения будут проглочены внутри возвращаемого Task
чего не ожидается Если вы используете.NET 4.0, это исключение будет переброшено в финализатор этого Task.
, Если вы используете.NET 4.5, он будет просто проигнорирован (а это может быть нежелательно).
Хочу убедиться, что я не несу никаких штрафов за то, что у меня слишком много тем или что-то в этом роде.
Я просто говорю это, чтобы прояснить ситуацию. Когда вы используете Task.Delay
Вы не крутите новые темы. Task
не всегда совпадает с тем, что новая нить вращается. В частности, здесь, Task.Delay
внутренне использует Timer
, поэтому нет никаких накладных расходов потока (кроме потока, который в настоящее время задерживается, если вы используете await
).
Предупреждение говорит вам, что вы получаете огонь и забываете поведение в месте, где вы можете не захотеть стрелять и забыть. Если вы действительно хотите выстрелить и забыть, и у вас нет проблем с продолжением, даже не зная, когда операция закончится или даже успешно завершилась, вы можете спокойно проигнорировать предупреждение.
Одним из отрицательных результатов освобождения задачи для запуска без ожидания являются сами предупреждения компилятора - 75 предупреждений компилятора сами по себе являются проблемой, и они скрывают реальные предупреждения.
Если вы хотите сообщить компилятору, что вы намеренно ничего не делаете с результатом задачи, вы можете использовать простой метод расширения, который ничего не делает, но удовлетворяет стремление компилятора к явности.
// Do nothing.
public static void Release(this Task task)
{
}
Теперь вы можете позвонить
UpdateCacheAsync(data).Release();
без каких-либо предупреждений компилятора.
https://gist.github.com/lisardggY/396aaca7b70da1bbc4d1640e262e990a
Рекомендуемый способ ASP.NET
HostingEnvironment.QueueBackgroundWorkItem(WorkItem);
...
async Task WorkItem(CancellationToken cancellationToken)
{
try { await ...} catch (Exception e) { ... }
}
КСТАТИ: Не перехват / перебрасывание потока, отличного от потока ASP.NET, может привести к сбою / перезапуску процесса сервера.