Соображения о том, что не следует ожидать Задачу в асинхронном методе

Я работаю над проектом веб-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, может привести к сбою / перезапуску процесса сервера.

Другие вопросы по тегам