Рекомендуется вызывать ConfigureAwait для всего кода на стороне сервера

когда у вас есть серверный код (т. е. некоторые ApiController) и ваши функции асинхронны-поэтому они возвращают Task<SomeObject> - считается ли лучшей практикой, что в любое время вы ждете функций, которые вы вызываете ConfigureAwait(false)?

Я читал, что он более эффективен, поскольку ему не нужно переключать контексты потоков обратно в исходный контекст потока. Однако, с ASP.NET Web Api, если ваш запрос поступает в один поток, и вы ждете какую-то функцию и вызываете ConfigureAwait(false) что может потенциально поместите вас в другой поток, когда вы возвращаете конечный результат вашего

4 ответов


обновление: ядро ASP.NET нет SynchronizationContext. Если вы на ASP.NET Core, не имеет значения, используете ли вы ConfigureAwait(false) или нет.

для ASP.NET "полный" или "классический" или что-то еще, остальная часть этого ответа все еще применима.

Оригинальный пост (для непрофильных ASP.NET):

это видео ASP.NET команда имеет лучшую информацию об использовании async on ASP.NET.

Я читал, что он более эффективен, поскольку ему не нужно переключать контексты потоков обратно в исходный контекст потока.

это верно для приложений пользовательского интерфейса, где есть только один поток пользовательского интерфейса, который вам нужно "синхронизировать".

In ASP.NET ситуация немного сложнее. Когда async метод возобновляет выполнение, он захватывает поток из ASP.NET пул нитей. Если вы отключите захват контекста с помощью ConfigureAwait(false), тогда поток просто продолжает выполнение метода напрямую. Если вы не отключите захват контекста, поток повторно войдет в контекст запроса и продолжит выполнение метода.

так ConfigureAwait(false) не сохраняет вам переход потока в ASP.NET; это избавляет вас от повторного ввода контекста запроса, но обычно это происходит очень быстро. ConfigureAwait(false) мог бы быть полезным, если вы пытаетесь сделать небольшое количество параллельной обработки запроса, но на самом деле TPL лучше подходит для большинства из этих сценариев.

однако, с ASP.NET веб-Api, если ваш запрос поступает в один поток, и вы ждете некоторой функции и вызываете ConfigureAwait(false), который потенциально может поместить вас в другой поток, когда вы возвращаете конечный результат своей функции ApiController.

на самом деле, просто делал await можете это сделать. Однажды ваш async метод попадает в await на метод блокируется, но нить возвращается в пул потоков. Когда метод готов к продолжению, любой поток выхватывается из пула потоков и используется для возобновления метода.

разница только ConfigureAwait делает внутри ASP.NET является ли этот поток входит в контекст запроса при возобновлении метода.

у меня есть больше справочной информации в моем статья MSDN на SynchronizationContext и меня async интро блоге.


краткий ответ на ваш вопрос: нет. Вы не должны звонить ConfigureAwait(false) на уровне приложения, как это.

TL;DR версия длинного ответа: Если вы пишете библиотеку, где вы не знаете своего потребителя и не нуждаетесь в контексте синхронизации (который вы не должны в библиотеке, я считаю), вы всегда должны использовать ConfigureAwait(false). В противном случае потребители вашей библиотеки могут столкнуться с блокировками, используя асинхронные методы блокирующим способом. Это зависит от ситуация.

вот немного более подробное объяснение важности ConfigureAwait метод (цитата из моего блога):

когда вы ожидаете метода с ключевым словом await, компилятор генерирует кучу кода от вашего имени. Одна из целей этого действие заключается в обработке синхронизации с потоком UI (или main). Ключ компонентом этой функции является SynchronizationContext.Current что возвращает контекст синхронизации для текущего потока. SynchronizationContext.Current is заполняется в зависимости от среда в которой вы находитесь. The GetAwaiter метод задачи ищет SynchronizationContext.Current. Если текущий контекст синхронизации не null, продолжение, которое передается этому ожидающему, получит отправлено обратно в контекст синхронизации.

при использовании метода, который использует новый асинхронный язык особенности, в блокировке моды, вы в конечном итоге с тупиком, если у вас есть доступный SynchronizationContext. Когда вы потребляете такие методы в блокирующем режиме (ожидание задачи с Wait метод или принимать результат сразу от свойства результата Задача), вы будете блокировать основной поток одновременно. Когда в конце концов задача завершается внутри этого метода в threadpool, это собирается вызвать продолжение для публикации обратно в основной поток потому что SynchronizationContext.Current доступен и захвачен. Но здесь есть проблема: поток пользовательского интерфейса будет заблокирован и у вас тупик!

кроме того, вот две большие статьи для вас, которые именно для вашего вопроса:

наконец, есть большое короткое видео от От Lucian Wischik именно на эту тему: методы асинхронной библиотеки должны рассмотреть возможность использования задачи.ConfigureAwait (false).

надеюсь, что это помогает.


самый большой недостаток, который я нашел с помощью ConfigureAwait (false), заключается в том, что культура потока возвращается к системному умолчанию. Если вы настроили культуру e.г...

<system.web>
    <globalization culture="en-AU" uiCulture="en-AU" />    
    ...

и вы размещаете на сервере, культура которого установлена в en-US, то вы найдете перед ConfigureAwait (false) называется CultureInfo.CurrentCulture вернется en-AU и после того, как вы получите en-US. т. е.

// CultureInfo.CurrentCulture ~ {en-AU}
await xxxx.ConfigureAwait(false);
// CultureInfo.CurrentCulture ~ {en-US}

Если ваше приложение делает что-либо, что требует культуры конкретное форматирование данных, тогда вам нужно будет помнить об этом при использовании ConfigureAwait(false).


у меня есть некоторые общие мысли о реализации Task:

  1. задача одноразовая, но мы не должен использовать using.
  2. ConfigureAwait был введен в 4.5. Task был введен в 4.0.
  3. .NET Threads всегда используется для потока контекста (см. C# через CLR book), но в реализации по умолчанию Task.ContinueWith они не b/c было осуществлено что переключатель контекста дорог и он повернут мимо по умолчанию.
  4. проблема заключается в том, что разработчик библиотеки не должен заботиться о том, нужен ли его клиентам поток контекста или нет, поэтому он не должен решать, течет ли контекст или нет.
  5. [добавлено позже] тот факт, что нет авторитетного ответа и надлежащей ссылки, и мы продолжаем бороться с этим, означает, что кто-то не сделал свою работу правильно.

у меня есть несколько посты на эту тему, но мое мнение - в дополнение к хороший ответ Tugberk - это это вы должны включить все API асинхронно и в идеале поток контекста . поскольку вы выполняете асинхронность, вы можете просто использовать продолжения вместо ожидания, поэтому взаимоблокировка не будет вызвана, поскольку ожидание не выполняется в библиотеке, и вы сохраняете поток, поэтому контекст сохраняется (например, HttpContext).

проблема в том, что библиотека предоставляет синхронный API, но использует другой асинхронный API-следовательно, вам нужно использовать Wait()/Result в коде.