FAQ по async/await и ConfigureAwait
4. Что делает ConfigureAwait(false)?
Метод
ConfigureAwait, принимающий параметр continueOnCapturedContext («продолжитьВЗахваченномКонтексте»), возвращает структуру ConfiguredTaskAwaitable, которая содержит специальным образом настроенный «ожидатель» (awaiter). Поскольку await может использоваться с любым типом, соответствующим паттерну awaitable (не только с Task напрямую), возврат другого типа вместо задачи позволяет изменить логику захвата контекста/планировщика (см. предыдущий пост) на что-то вроде этого: object scheduler = null;Другими словами, получая значение
if (continueOnCapturedContext)
{
// захват контекста/планировщика
}
false, объект не захватывает текущий контекст/планировщик, даже если они существуют.Зачем использовать ConfigureAwait(false)?
Этот метод используется, чтобы избежать принудительного вызова метода обратного вызова в исходном контексте или планировщике. Это даёт несколько преимуществ:
1. Улучшает производительность. Требуется дополнительная работа, чтобы поставить в очередь метод обратного вызова вместо того, чтобы просто вызывать его. Кроме того, некоторые оптимизации времени выполнения, не могут быть использованы. Иногда даже проверка текущих контекста и планировщика (что включает в себя доступ к статическим потокам) могут добавить измеримые накладные расходы. Если код после
await фактически не требует запуска в исходном контексте, использование ConfigureAwait(false) поможет избежать всех этих затрат.2. Позволяет избежать взаимных блокировок. Взаимная блокировка чаще всего возникает при использовании кода, блокирующего текущий поток (при вызове
.Wait(), .Result или .GetAwaiter().GetResult() на задаче, внутри которой используется await). Если в текущем контексте явно или неявно ограничено число параллельных операций, либо если код блокирует UI-поток, получается ситуация, когда контекст/поток заблокирован в ожидании завершения асинхронной задачи, а метод обратного вызова этой задачи ставится в очередь для вызова на заблокированном контексте/потоке и поэтому не может быть выполнен (взаимная блокировка). Такая ситуация может возникать, когда ресурсы ограничены любым способом и так или иначе оказываются заняты. Если задача использует ConfigureAwait(false), метод обратного вызова не ставится в очередь в исходный контекст, что позволяет избежать сценариев взаимоблокировки.Зачем использовать ConfigureAwait(true)?
Поскольку захват контекста происходит по умолчанию, то
ConfigureAwait(true) практически никогда не используется, за исключением случаев, когда вы хотите показать, что вы намеренно не используете ConfigureAwait(false) (например, чтобы отключить предупреждения статического анализа кода). Метод ConfigureAwait принимает логическое значение, поскольку существуют некоторые нишевые ситуации, в которых вы хотите передать переменную для управления конфигурацией. Но в 99% случаев используется жестко закодированное значение false. Поэтому вызовы ConfigureAwait(true) можно безболезненно удалять.Источник: https://devblogs.microsoft.com/dotnet/configureawait-faq/