Лучшая практика для вызова ConfigureAwait для всего кода на стороне сервера

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

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

Я набрал пример того, о чем я говорю ниже:

 public class CustomerController : ApiController { public async Task Get(int id) { // you are on a particular thread here var customer = await SomeAsyncFunctionThatGetsCustomer(id).ConfigureAwait(false); // now you are on a different thread! will that cause problems? return customer; } } 

4 Solutions collect form web for “Лучшая практика для вызова ConfigureAwait для всего кода на стороне сервера”

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

Для ASP.NET «Полный» или «Классик» или что-то еще, остальная часть этого ответа по-прежнему применяется.

Исходный пост (для неосновного ASP.NET):

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

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

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

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

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

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

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

Единственное отличие, которое ConfigureAwait делает в ASP.NET, заключается в том, входит ли этот stream в контекст запроса при возобновлении метода.

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

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

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

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

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

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

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

  • Идеальный рецепт, чтобы стрелять в ногу – Завершение с помощью тупика Использование асинхронных функций языка C # 5.0
  • Асинхронные .NET-клиентские библиотеки для вашего HTTP-API и осознание плохих эффектов async / await

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

Надеюсь это поможет.

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

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

У меня есть несколько сообщений по этому вопросу, но мой ответ – в дополнение к приятному ответу Тугберка – заключается в том, что вы должны превратить все API асинхронные и идеально использовать контекст. Поскольку вы выполняете async, вы можете просто использовать продолжения, а не ждать, так что никакой взаимоблокировки не будет, потому что в библиотеке не происходит никакого ожидания, и вы сохраняете stream, чтобы контекст сохранялся (например, HttpContext).

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

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

   ... 

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

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

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

  • ASP.NET Web API OperationCanceledException, когда браузер отменяет запрос
  • Как отменить задачу ждут после периода ожидания
  • Как отменить задачу в ожидании?
  • Что касается использования Task.Start (), Task.Run () и Task.Factory.StartNew ()
  • Перекачка сообщений StaTaskScheduler и STA
  • В чем разница между задачей и streamом?
  • Parallel.ForEach и async-wait
  • Использование async / ожидание нескольких задач
  • Ожидание против Задачи. Подождите - Тупик?
  • Как выполнить задачу на фоне wpf, когда вы можете предоставить отчет и разрешить аннулирование?
  • Метод общего назначения FromEvent
  • Давайте будем гением компьютера.