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


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

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

4   426   2012-11-21 12:24:49

4 ответа:

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

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

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

это видео от ASP.NET команда имеет лучшую информацию об использовании async ВКЛ 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 на метод блокируется, но thread возвращается в пул потоков. Когда метод готов к продолжению, любой поток выхватывается из пула потоков и используется для возобновления метода.

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

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

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

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

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

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

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

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

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

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

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

<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 в коде.