只要你的web.config设置正确 https://web.archive.org/web/20170324060430/https://blogs.msdn.microsoft.com/webdev/2012/11/19/all-about-httpruntime-targetframework/, async
/await
完美配合HttpContext.Current
。我建议设置httpRuntime
targetFramework
to 4.5
删除所有“怪癖模式”行为。
一旦完成,简单明了async
/await
会工作得很好。仅当您在另一个线程上工作或者您的await
代码不正确。
一、“其他线程”问题;这是您链接到的博客文章中的第二个问题。这样的代码当然无法正常工作:
async Task FakeAsyncMethod()
{
await Task.Run(() =>
{
var user = _userService.Current;
...
});
}
这个问题实际上与异步代码无关;它与从(非请求)线程池线程检索上下文变量有关。如果您尝试同步执行此操作,也会出现完全相同的问题。
核心问题是异步版本正在使用fake异步。这不合适,尤其是在 ASP.NET 上。解决方案是简单地删除假异步代码并使其同步(或真正的异步,如果它实际上有真正的异步工作要做):
void Method()
{
var user = _userService.Current;
...
}
链接博客中推荐的技术(包装HttpContext
并将其提供给工作线程)是极其危险的。HttpContext
被设计为一次只能从一个线程访问,并且 AFAIK 根本不是线程安全的。因此,在不同的线程之间共享它会带来巨大的伤害。
If the await
代码不正确,那么会导致类似的问题。ConfigureAwait(false)
是库代码中常用的一种技术,用于通知运行时它不需要返回到特定上下文。考虑这段代码:
async Task MyMethodAsync()
{
await Task.Delay(1000).ConfigureAwait(false);
var context = HttpContext.Current;
// Note: "context" is not correct here.
// It could be null; it could be the correct context;
// it could be a context for a different request.
}
在这种情况下,问题就很明显了。ConfigureAwait(false)
告诉 ASP.NET 当前方法的其余部分不需要上下文,然后它立即访问该上下文。然而,当您开始在接口实现中使用上下文值时,问题就不那么明显了:
async Task MyMethodAsync()
{
await Task.Delay(1000).ConfigureAwait(false);
var user = _userService.Current;
}
这段代码同样错误,但没有那么明显错误,因为上下文隐藏在接口后面。
因此,一般准则是:use ConfigureAwait(false)
if you know该方法不依赖于其上下文(直接或间接);否则,请勿使用ConfigureAwait
。如果在您的设计中可以接受让接口实现在其实现中使用上下文,那么任何调用接口方法的方法都应该not use ConfigureAwait(false)
:
async Task MyMethodAsync()
{
await Task.Delay(1000);
var user = _userService.Current; // works fine
}
只要您遵循该准则,async
/await
将与完美配合HttpContext.Current
.