为什么HttpContext.Current在带有ConfigureAwait的异步/等待中不为null

时间:2019-03-09 18:44:00

标签: c# async-await configureawait

我有一个从控制器调用的库异步函数。我期望HttpContext.Current在所有地方都使用ConfigureAwait(false)等待后为null,但在控制器中不为null。有人可以解释为什么吗?

//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 个答案:

答案 0 :(得分: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为什么不为空的原因。