Когда у вас есть код на стороне сервера (т.е. некоторая ApiController
), и ваши функции асинхронны, поэтому они возвращают Task<SomeObject>
- считается ли это лучшей практикой, что в любое время вы ожидаете функции, которые вы называете ConfigureAwait(false)
?
Я прочитал, что он более эффективен, поскольку ему не нужно переключать контексты потоков обратно в исходный контекст потока. Тем не менее, с ASP.NET Web Api, если ваш запрос поступает в один поток, и вы ждете некоторую функцию и вызываете ConfigureAwait(false)
, который потенциально может поместить вас в другой поток, когда вы возвращаете окончательный результат вашего ApiController
функция.
Я набрал пример того, о чем я говорю ниже:
public class CustomerController : ApiController
{
public async Task<Customer> 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;
}
}
Обновление: У ASP.NET Core нет SynchronizationContext
. Если вы используете ASP.NET Core, неважно, используете ли вы ConfigureAwait(false)
или нет.
Для ASP.NET "Полный" или "Классик" или что-то еще, остальная часть этого ответа по-прежнему применяется.
Исходный пост (для неосновного ASP.NET):
Это видео команды ASP.NET имеет лучшую информацию об использовании async
на ASP.NET.
Я читал, что он более эффективен, поскольку ему не нужно переключать контексты потоков обратно в исходный контекст потока.
Это верно в приложениях UI, где есть только один поток пользовательского интерфейса, на который вы должны "синхронизировать" обратно.
В ASP.NET ситуация немного сложнее. Когда метод async
возобновляет выполнение, он захватывает поток из пула потоков ASP.NET. Если вы отключите захват контекста с помощью ConfigureAwait(false)
, поток просто продолжит выполнение метода напрямую. Если вы не отключите захват контекста, тогда поток снова войдет в контекст запроса и продолжит выполнение метода.
Итак, ConfigureAwait(false)
не сохраняет вас в потоке в ASP.NET; это избавляет вас от повторного ввода контекста запроса, но это, как правило, очень быстро. ConfigureAwait(false)
может быть полезна, если вы пытаетесь выполнить небольшую параллельную обработку запроса, но на самом деле TPL лучше подходит для большинства этих сценариев.
Однако, с ASP.NET Web Api, если ваш запрос поступает в один поток, и вы ждете некоторую функцию и вызываете ConfigureAwait (false), который потенциально может поставить вас на другой поток, когда вы возвращаете окончательный результат ваша функция ApiController.
Собственно, просто сделать await
можно. Когда ваш метод async
попадает в await
, метод блокируется, но поток возвращается в пул потоков. Когда метод готов к продолжению, любой поток вырывается из пула потоков и используется для возобновления метода.
Единственное отличие ConfigureAwait
в ASP.NET заключается в том, входит ли этот поток в контекст запроса при возобновлении метода.
У меня есть дополнительная информация в моей статье MSDN на SynchronizationContext
и async
вступление в блог.
Краткий ответ на ваш вопрос: Нет. Вы не должны называть ConfigureAwait(false)
на уровне приложения таким образом.
TL; DR-версия длинного ответа: если вы пишете библиотеку, где вы не знаете своего потребителя, и вам не нужен контекст синхронизации (который вы не должны в библиотеке, я считаю), вы всегда должны используйте ConfigureAwait(false)
. В противном случае потребители вашей библиотеки могут столкнуться с взаимоблокировками, блокируя ваши асинхронные методы. Это зависит от ситуации.
Ниже приведено более подробное объяснение важности метода ConfigureAwait
(цитата из моего сообщения в блоге):
Когда вы ждёте метод с ключевым словом await, компилятор генерирует кучу кода для вас. Одна из целей этого действие - обрабатывать синхронизацию с потоком пользовательского интерфейса (или основного). Ключ компонентом этой функции является
SynchronizationContext.Current
, которая получает контекст синхронизации для текущего потока.SynchronizationContext.Current
заполняется в зависимости от в которой вы находитесь. МетодGetAwaiter
Task ищетSynchronizationContext.Current
. Если текущий контекст синхронизации не null, продолжение, которое передается этому awaiter, получит отправлен обратно в этот контекст синхронизации.При использовании метода, который использует новый асинхронный язык функции, в блокирующем режиме, вы окажетесь в тупике, если у вас есть доступный SynchronizationContext. Когда вы потребляете такие методы в блокирующем режиме (ожидание задачи с ожиданием метода или получения результата непосредственно из свойства Result Задача), вы будете блокировать основной поток одновременно. когда в конечном итоге задача завершается внутри этого метода в threadpool, это собирается вызвать продолжение, чтобы отправить обратно в основной поток потому что
SynchronizationContext.Current
доступен и захвачен. Но здесь есть проблема: поток пользовательского интерфейса заблокирован, и у вас есть тупиковый!
Кроме того, вот две большие статьи для вас, которые именно для вашего вопроса:
Наконец, есть отличное короткое видео из Lucian Wischik именно по этой теме: Методам библиотеки Async следует рассмотреть возможность использования Task.ConfigureAwait(false).
Надеюсь, что это поможет.
Task
обходит стек, чтобы получить SynchronizationContext
, что неправильно. SynchronizationContext
захватывается перед вызовом Task
а затем остальная часть кода продолжается в SynchronizationContext
если SynchronizationContext.Current
не равен NULL.
Самый большой откат, который я нашел с помощью 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).
ConfigureAwait(false)
.
У меня есть общие мысли о реализации Task
:
using
.ConfigureAwait
был введен в 4.5. Task
был введен в 4.0.Task.ContinueWith
они не выполняли b/c, а контекстный переключатель был дорогостоящим и по умолчанию отключен.У меня есть несколько сообщений на эту тему, но мой прием - помимо хорошего ответа Tugberk - заключается в том, что вам следует превратите все API в асинхронный и идеальный поток контекста.. Поскольку вы выполняете async, вы можете просто использовать продолжения, а не ждать, так что никакой взаимоблокировки не будет, потому что в библиотеке нет ожидания, и вы держите поток таким образом, что контекст (например, HttpContext).
Проблема в том, что библиотека предоставляет синхронный API, но использует другой асинхронный API, поэтому вам нужно использовать Wait()
/Result
в вашем коде.
Task.Dispose
если хотите; Вам просто не нужно подавляющее большинство времени. 2) Task
была введена в .NET 4.0 как часть TPL, которая не нуждалась в ConfigureAwait
; когда async
была добавлена, они повторно использовали существующий тип Task
вместо того, чтобы придумывать новое Future
.
Task
s; «контекст», управляемый ContinueWith
представляет собой SynchronizationContext
или TaskScheduler
. Эти различные контексты подробно объясняются в блоге Стивена Туба .
ContinueWith
по умолчанию - точка. Данные TSA не копируются - пожалуйста, докажите, что я не прав, если вы думаете иначе. Вы можете проверить это, посмотрев наHttpContext.Current
. Вот почему мы проходим через обручи и обручи, чтобы течь это.HttpContext.Current
через ASP.NETSynchronizationContext
, которыйHttpContext.Current
по умолчанию, когда выawait
, но не передается черезContinueWith
. Ото, контекст исполнения ( в том числе ограничений безопасности) контекст упоминается в CLR через C #, и она потекла обаContinueWith
иawait
(даже если вы используетеConfigureAwait(false)
).