我有一个从控制器调用的库异步函数。我期望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");
}
}
答案 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为什么不为空的原因。