为什么 HttpContext.Current 在 async/await 中使用 ConfigureAwait 不为空

Why HttpContext.Current is not null in async/await with ConfigureAwait

我有一个从控制器调用的库异步函数。我希望 HttpContext.Current 在随处使用 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");
    }
}

虽然比这复杂得多,但您可以将 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 不为空。