Как работают async и await в C#?
Что проверяют
Понимаете ли вы, что асинхронность это не «ещё один поток», а способ освободить текущий на время ожидания. Дальше спрашивают про контекст синхронизации и дедлоки.
Как отвечать
async разрешает писать await внутри метода и заставляет компилятор превратить тело в конечный автомат. await не блокирует поток: он подписывается на завершение задачи и возвращает управление вызывающему, а остаток метода выполняется продолжением, когда результат готов.
Смысл в масштабируемости. Запрос к базе это миллисекунды ожидания сети, и всё это время поток мог бы обслуживать другие запросы:
public async Task<User> GetAsync(int id)
{
var row = await _db.QuerySingleAsync(id); // поток свободен, пока ждём
return Map(row);
}Правила, которые проверяют чаще всего:
- возвращайте
TaskилиTask<T>.async voidтолько для обработчиков событий: его нельзя дождаться, а исключение из него улетает в необработанные; - не смешивайте с блокировкой.
.Resultи.Wait()поверх асинхронного кода в ASP.NET или WPF дают дедлок: продолжение ждёт контекст, а контекст занят ожиданием; - в библиотеке пишите
ConfigureAwait(false), продолжению чужой контекст синхронизации не нужен; Task.Runэто про нагрузку на процессор. Оборачивать в него запрос к базе значит занять поток ожиданием, то есть сделать ровно то, от чего уходили.
Где спотыкаются
Говорят, что async создаёт поток. Для операций ввода-вывода потока нет вообще, есть уведомление от драйвера.
Ждут ускорения одного запроса. Асинхронность не делает его быстрее, она освобождает поток.
Пишут последовательные await там, где задачи независимы, вместо Task.WhenAll.
Не знают, что исключение внутри задачи всплывает только на await и до него молчит.