Почему HttpContext.Current не равен нулю в async / await с помощью ConfigureAwait

#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.