Why is HttpContext.Current null after await?
今天在对项目代码进行异步化改进的时候,遇到一个奇怪的问题(莫笑,以前没遇过),正如标题一样,HttpContext.Current 在 await 异步执行之后,就会变为 null。
演示代码:
public async Task<IEnumerable<string>> Get()
{
await DoWaitAsync();
DoWork();
return new string[] { "value1", "value2" };
}
private Task DoWaitAsync()
{
return
Task.Factory.StartNew(
() =>
{
// null !!
var httpCtx = System.Web.HttpContext.Current;
Thread.Sleep(1000);
});
}
public void DoWork()
{
//Not null
var httpCtx = System.Web.HttpContext.Current;
}
HttpContext.Current 这个东西,我们并不陌生,在进行 ASP.NET 应用程序开发的时候,我们经常会用到,比如获取当前请求的一些值,首先它是一个线程静态属性(thread-static variable),注意其中的关键字:当前请求和线程,也就是说它是和请求线程相关的,在 ASP.NET 应用程序中,一个请求线程会贯穿整个请求过程,所以我们可以在这个请求的任何地方,都可以访问到 HttpContext.Current,这也就是它的“强大之处”,但是如果涉及到异步多线程呢?就不是这么回事了,因为 HttpContext.Current 依附的是当前请求的主线程,当我们使用 await 异步执行一些代码的时候,再次执行下面的代码,其实就不是当前请求线程了,所以我们再次访问 HttpContext.Current 的时候,就变为 null 了,这个问题告诫我们,ASP.NET 应用程序中,如果进行异步化,使用 HttpContext.Current 一定要小心谨慎。
- 需要注意的是:HttpContext.Current 在没有进行 await 操作的时候,都是可以获取到值的。
如果我们的 ASP.NET 应用程序进行了异步化,然后还必须用到 HttpContext.Current,那我们该怎么解决这个问题?
解决的方式有很多,如果应用程序很简单,我们可以在 await 操作之前,先用变量存储 HttpContext.Current,用到的地方直接用这个变量就行了,当然这不是一个“解决问题”的方法,还有一种是用 Cache,可以参考:system-web-httpcontext-current-nulls-itself-after-checking-for-a-cache,我觉得这种方式也是“瞎忽悠”,没有从根本问题上进行解决。
其实想想问题的根源,就是如何在多个线程中共享一个 HttpContext.Current,这个在 MSDN 中表述为:共享/同步上下文(Synchronization Context)
You can use the TaskScheduler.FromCurrentSynchronizationContext method to specify that a task should be scheduled to run on a particular thread. This is useful in frameworks such as Windows Forms and Windows Presentation Foundation where access to user interface objects is often restricted to code that is running on the same thread on which the UI object was created. For more information, see How to: Schedule Work on a Specified Synchronization Context.
那我们如何在 ASP.NET 应用程序中,进行运用呢?答案很简单,我们只需要在 web.config 中指定 targetFramework 版本为 4.5 即可:
<httpRuntime targetFramework="4.5" />
或者在 appSettings 中添加如下 key(测试可用):
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
参考资料:
相关文章
- [PHP] PHP5中的写时复制change on write
- [PHP] 使用PHP迭代表示二叉树的查找
- [PHP]利用PHP的引用生成树的结构
- [PHP] PHP7以上版本的引用计数不同的困惑
- [MySQL]ANALYZE TABLE 更新索引基数
- [Docker] 使用docker inspect查看宿主机与容器的共享目录
- [MySQL] 理解InnoDB并发高的原因
- [HTTP]HTTP/1.1 协议Expect: 100-continue
- [MySQL]mysql binlog回滚数据
- [PHP]PHP中申明 declare(strict_types=1)的作用
- Redis持久化: RDB&AOF
- [PHP] php作为websocket的客户端实时读取推送日志文件
- [MySQL] in 子查询出现DEPENDENT SUBQUERY问题
- [MySQL] 导入数据时防止出现乱码
- [MySQL] group by 聚合函数的原理和聚合限制原因SELECT list is not in GROUP BY clause and contains nonaggregated column
- [PHP] websocket协议的生成
- [MySQL]mysql的ANY_VALUE()函数 解决 ONLY_FULL_GROUP_BY 模式
- [PHP] 框架中.env文件的加载过程
- [PHP] PHP7已经删除了preg_replace的e修饰符
- [日常]解决Connection to `ssl://pecl.php.net:443' failed