#c# #async-await #configureawait
#c# #async-await #configureawait
Вопрос:
У меня есть библиотечная асинхронная функция, вызываемая из контроллера. Я ожидал, что HttpContext.Current будет равен нулю после await с ConfigureAwait (false) везде, но в контроллере это не равно нулю. Кто-нибудь может объяснить почему?
//in libraby
public class MyClass
{
public async Task WaitAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
var httpContext = System.Web.HttpContext.Current; // null, OK
}
}
public class HomeController : Controller
{
public async Task<ActionResult> Index()
{
var class1 = new MyClass();
await class1.WaitAsync();
var httpContext = System.Web.HttpContext.Current; // not null, WHY???
return View("Index");
}
}
Комментарии:
1.
await class1.WaitAsync();
нет.ConfigureAwait(false)
, так почему вы ожидаете, что HttpContext будет равен нулю?2. @KevinGosse потому что весь код после await выполняется в потоке из пула потоков. Я проверил, что ManagedThreadId совпадает.
3. ConfigureAwait(false) действует только до окончания вызова метода.
Ответ №1:
Хотя это намного сложнее, чем это, вы можете представить await
как своего рода ContinueWith
. Итак, если вы напишете, например:
DoSomeStuff();
await WaitAsync()
DoMoreStuff();
Он переписывается в:
DoSomeStuff();
WaitAsync().ContinueWith(_ => DoMoreStuff());
.ConfigureAwait
задает контекст, в котором будет выполняться продолжение. При ConfigureAwait(true)
(по умолчанию) продолжение будет выполняться в том же контексте, что и вызывающий объект. При ConfigureAwait(false)
продолжение будет выполняться в инвариантном контексте по умолчанию, в пуле потоков.
С нашим предыдущим упрощением давайте представим, что ConfigureAwait(true)
будет переписано в ContinueWithSameContext
и ConfigureAwait(false)
в ContinueWithThreadPool
.
Теперь, что произойдет, если у нас будут вложенные методы? Например, ваш код:
public async Task WaitAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
var httpContext = System.Web.HttpContext.Current; // null, OK
}
public async Task<ActionResult> Index()
{
var class1 = new MyClass();
await class1.WaitAsync();
var httpContext = System.Web.HttpContext.Current; // not null, WHY???
return View("Index");
}
Это тоже переписано:
public Task WaitAsync()
{
return Task.Delay(TimeSpan.FromSeconds(1))
.ContinueWithThreadPool(_ =>
{
var httpContext = System.Web.HttpContext.Current; // null, OK
});
}
public Task<ActionResult> Index()
{
var class1 = new MyClass();
return class1.WaitAsync().ContinueWithSameContext(_ =>
{
var httpContext = System.Web.HttpContext.Current; // not null, WHY???
return View("Index");
}
}
Переписанный таким образом, вы видите, что продолжение WaitAsync
будет выполняться в том же контексте, что и Task<ActionResult> Index()
, объясняя, почему HttpContext не равен null.