Как получить результат асинхронной задачи в C#?
Что проверяют
Знаете ли вы правильный способ и понимаете ли, чем опасны блокирующие альтернативы.
Как отвечать
Правильный способ один: await.
var result = await GetDataAsync();Он не блокирует поток: метод возвращает управление вызывающему, а продолжение выполняется, когда задача завершится.
Блокирующие способы (task.Result, task.Wait(), GetAwaiter().GetResult()) держат поток и в контексте с синхронизацией (классический ASP.NET, интерфейс WinForms и WPF) приводят к взаимной блокировке: продолжение ждёт занятый контекст, а контекст ждёт завершения задачи. В ASP.NET Core контекста нет, но поток из пула всё равно тратится впустую, и под нагрузкой это заканчивается исчерпанием пула.
Что ещё стоит назвать:
- исключения: при
awaitвылетает исходное исключение, при.Resultоно приходит завёрнутым вAggregateException; - несколько задач:
await Task.WhenAll(t1, t2)для всех сразу,WhenAnyдля первого завершившегося. Задачи создаются до ожидания, иначе они выполнятся последовательно; - отмена и таймаут:
CancellationTokenпередаётся в саму операцию, а не проверяется после; ConfigureAwait(false)в библиотечном коде, чтобы продолжение не возвращалось в исходный контекст;ValueTaskдля горячих путей, где результат часто готов синхронно и лишнее выделение памяти нежелательно;async voidтолько для обработчиков событий: его исключение нельзя поймать, оно роняет процесс.
Где спотыкаются
Пишут .Result в контроллере и получают взаимную блокировку или исчерпание пула.
Ставят await в цикле там, где запросы независимы, и превращают параллельную работу в последовательную.
Считают, что async сам по себе создаёт поток и ускоряет вычисления.