我所知道的HttpContext.Current
在MVC中,HttpContext.Current是比较常见的对象,可以用它来进行Session,Cache等的保存等。但是它并不是无处不在的,下面就慢慢来揭开它的面纱。
当我们向服务端发送请求的时候,页面会响应我们的请求,比如我们访问A页面,那么服务端就会把A页面通过处理后返回给我们,访问B页面,过程同样。在这里,我们访问A页面和访问B页面,总共进行了2次请求,这两次请求会不会是在同一个页面线程中呢?因为Asp.net模型本身就是多线程模式的,那么让我们来做一个实验:
首先,创建一个Asp.net项目,新建一个Default.aspx主页面,然后新建一个About.aspx从页面,在Default.aspx页面中,代码如下:
1: protected void Page_Load(object sender, EventArgs e)
2: {
3: Response.Write("Default页面线程ID:" + Thread.CurrentThread.ManagedThreadId);
4: HttpContext.Current.Cache.Insert("test", "TestValue", null, DateTime.Now.AddMinutes(120),System.Web.Caching.Cache.NoSlidingExpiration);
5: }
在代码中,我们打印出了当前页面的线程ID并做了一个简单的缓存.
在About.aspx页面中,代码如下:
1: protected void Page_Load(object sender, EventArgs e)
2: {
3: bool flag = HttpContext.Current == null;
4: Response.Write("About页面线程ID:" + Thread.CurrentThread.ManagedThreadId.ToString(+"<br/>");
5: Response.Write("About页面的Httpcontext对象:"+(flag?"未创建":"已经创建")+"<br/>");
6: Response.Write("About页面获取的缓存值为:"+HttpContext.Current.Cache["test"]+"<br/>");
7: }
在代码中,我们同样打印出了当前页面的线程ID并获取之前的缓存:
得到的结果如下:
1: Default页面线程ID:4
3: About页面线程ID:8
4: About页面的Httpcontext对象:已经创建
5: About页面获取的缓存值为:TestValue
从代码输出结果,我们可以看出,两次请求分别被不同的线程处理,HttpContext.Current在这两个线程中是可用的,为什么跨线程后还能够使用呢?这个后面我们再来讲解。
下面再看两个例子,在APM模式处理中和子线程处理中,看看HttpContext.Current是否依然能够访问:
首先是APM模式的例子:
1: private void DO()
2: {
3: System.Threading.Thread.Sleep(1000);
4: Response.Write(HttpContext.Current==null?"没有创建":"已经创建");
5: }
6:
7: private void BeginDO()
8: {
9: Action action = new Action(DO);
10: action.BeginInvoke((iar) =>
11: {
12: Action endAction = (Action)iar.AsyncState;
13: endAction.EndInvoke(iar);
14: }, action);
15: }
做好之后,让我们运行起来,结果在执行
Response.Write(HttpContext.Current==null?"没有创建":"已经创建");
这句话的时候报错,提示:响应在此上下文中不可用。看来,在APM中,是不可用的。
那么在子线程中呢?
1: private void ThreadDo()
2: {
3: new Thread(new ThreadStart(DO)).Start();
4: }
让我们执行,看看结果,不幸的是,当执行到同样那句话的时候报错,提示:响应在此上下文中不可用。
为什么在页面中,跨线程可用,但是在APM模式或者是创建了子线程的场景中不能用呢?
就让我们来剖析一下源代码:
首先,HttpContext.Current源码:
1: // System.Web.HttpContext
2: /// <summary>Gets or sets the <see cref="T:System.Web.HttpContext" /> object for the current HTTP request.</summary>
3: /// <returns>The <see cref="T:System.Web.HttpContext" /> for the current HTTP request.</returns>
4: public static HttpContext Current
5: {
6: get
7: {
8: return ContextBase.Current as HttpContext;
9: }
10: set
11: {
12: ContextBase.Current = value;
13: }
14: }
我们看到,是通过ContextBase.Current返回,让我们继续深究下去:
1: // System.Web.Hosting.ContextBase
2: internal static object Current
3: {
4: get
5: {
6: return CallContext.HostContext;
7: }
8: [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
9: set
10: {
11: CallContext.HostContext = value;
12: }
13: }
走到这里我们看到,是通过CallContext.HostContext对象返回,继续深究:
1: // System.Runtime.Remoting.Messaging.CallContext
2: /// <summary>Gets or sets the host context associated with the current thread.</summary>
3: /// <returns>The host context associated with the current thread.</returns>
4: /// <exception cref="T:System.Security.SecurityException">The immediate caller does not have infrastructure permission. </exception>
5: public static object HostContext
6: {
7: [SecurityCritical]
8: get
9: {
10: IllogicalCallContext illogicalCallContext = Thread.CurrentThread.GetIllogicalCallContext();
11: object hostContext = illogicalCallContext.HostContext;
12: if (hostContext == null)
13: {
14: LogicalCallContext logicalCallContext = CallContext.GetLogicalCallContext();
15: hostContext = logicalCallContext.HostContext;
16: }
17: return hostContext;
18: }
19: [SecurityCritical]
20: set
21: {
22: if (value is ILogicalThreadAffinative)
23: {
24: IllogicalCallContext illogicalCallContext = Thread.CurrentThread.GetIllogicalCallContext();
25: illogicalCallContext.HostContext = null;
26: LogicalCallContext logicalCallContext = CallContext.GetLogicalCallContext();
27: logicalCallContext.HostContext = value;
28: return;
29: }
30: LogicalCallContext logicalCallContext2 = CallContext.GetLogicalCallContext();
31: logicalCallContext2.HostContext = null;
32: IllogicalCallContext illogicalCallContext2 = Thread.CurrentThread.GetIllogicalCallContext();
33: illogicalCallContext2.HostContext = value;
34: }
35: }
看到了什么?从字面意思,我们可以看到:如果当前线程中存在与之相关的对象,则直接返回,否则就创建一个关联的对象并返回。
那么,也就是说,虽然请求的页面的线程变了,但是由于HttpContext.Current被当做相关对象被创建,所以可以在不同的页面中获取其实例。
但是为什么在APM模式或者子线程场景中不能访问呢?由于APM模式或者子线程场景的处理是被操作系统所接管,因为apm模式需要操作系统分配线程来进行异步处理,子线程也是由操作系统调配,这些与当前的请求已经没有关系,所以会出现无响应的情况。下面一幅图代表我对这个的理解:
另外,引用一下Fish-li的文章,Httpcontext.current在如下场景中是不可用的,我们看到,这些场景操作都是被操作系统所接管:
1. 定时器的回调。
2. Cache的移除通知。
3. APM模式下异步完成回调。
4. 主动创建线程或者将任务交给线程池来执行。
最后,有人说,那我可以把HttpContext.current对象全局缓存,然后放到子线程中使用可以么?我只能说,仍然不可以,仍然会出现“响应在此上下文中不可用”的错误提示。你不信可以试试看。
由于HttpContext.Current对象会被关联创建,所以,全局缓存他是没必要的。
我所知道的HttpContext.Current的更多相关文章
- HttpContext.Current:异步模式下的疑似陷阱之源
最近园子里首页有好几篇文章都是讲异步编程的,尤其是几篇讲博客园自身的异步化建设的文章,看了以后很有收获. 闲暇之余再重新查查资料温故知新学习一遍,重新认识了SynchronizationContext ...
- 异步 HttpContext.Current 为空null 另一种解决方法
1.场景 在导入通讯录过程中,把导入的失败.成功的号码数进行统计,然后保存到session中,客户端通过轮询显示状态. 在实现过程中,使用的async调用方法,出现HttpContext.Curren ...
- 解决Asp.net Mvc中使用异步的时候HttpContext.Current为null的方法
在项目中使用异步(async await)的时候发现一个现象,HttpContext.Current为null,导致一系列的问题. 上网查了一些资料后找到了一个对象: System.Threading ...
- System.Web.HttpContext.Current.Session为NULL解决方法
http://www.cnblogs.com/tianguook/archive/2010/09/27/1836988.html 自定义 HTTP 处理程序,从IHttpHandler继承,在写Sys ...
- Why is HttpContext.Current null after await?
今天在对项目代码进行异步化改进的时候,遇到一个奇怪的问题(莫笑,以前没遇过),正如标题一样,HttpContext.Current 在 await 异步执行之后,就会变为 null. 演示代码: pu ...
- 在ASP.NET Core中怎么使用HttpContext.Current
一.前言 我们都知道,ASP.NET Core作为最新的框架,在MVC5和ASP.NET WebForm的基础上做了大量的重构.如果我们想使用以前版本中的HttpContext.Current的话,目 ...
- HttpContext.Current.Session.SessionID相关问题及备忘
今天Tony提到说我们系统中会利用如下代码来判断用户是否过期. if (string.IsNullOrEmpty(UserContext.ConnectionSessionId)) { LogUIFa ...
- 【C#】关于HttpContext.Current.Request.QueryString 你要知道点
HttpContext.Current.Request.QueryString[ ]括号中是获取另一个页面传过的的参数值 HttpContext.Current.Request.Form[“ID”]· ...
- HttpContext.Current.User is null after installing .NET Framework 4.5
故障原因:从framework4.0到framework4.5的升级过程中,原有的form认证方式发生了变化,所以不再支持User.Identity.Name原有存储模式(基于cookie),要恢复这 ...
随机推荐
- swift2.2当中的inout参数的使用
在Swift中,初次接触inout关键字以及它的用法,可能会让我们想起C/C++中的指针,但实际上Swift中inout只不过是按值传递,然后再写回原变量,而不是按引用传递: An in-out pa ...
- 启动app时全屏显示Default.png
一直很喜欢MJ的讲课视频,今天看了他的博客学到了些东西顺便记录下来. 在默认情况下,app显示Default.png时并非真正的"全屏显示",因为顶部的状态栏并没有被隐藏,比如下面 ...
- ruby语言是什么东西
1.简介 Ruby是日本的Yukihiro Matsumoto写的,简单易学的面向对象的脚本语言,像perl一样,有丰富的文字处理.系统管理等丰富 功能,但是ruby要简单,容易理解和扩充.跟p ...
- UVa 107 - The Cat in the Hat (找规律,注意精度)
题目来源:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=3&pa ...
- svn conflict
安装svn apt-get install subversion 当前两个人都更新版本为version1 A修改了monitor.txt文件 提交后版本为version2 B也修改了monitor.t ...
- 《SQL Server企业级平台管理实践》读书笔记——当我们的备份都已经损坏的时候该怎么办
作为数据库管理员最最痛苦的莫过于,当数据库宕机的时候需要找备份,但在这个时候突然发现备份文件也是坏的,这就意味着数据会丢失,为此可能会丢掉职位,饭碗不保,所以为此,我们一定要保证好备份的完整性,一般发 ...
- Google自定义搜索引擎
本文主要介绍如何通过Google的API来定义自己的搜索引擎,并将Google搜索框嵌入到自己的web页面.另外,分析了自定义搜索引擎请求数据的url,模拟请求并获取搜索的结果. 1 写在前面 前段时 ...
- HashMap与TreeMap的区别
首先描述下什么是Map. 在数组中我们是通过数组的下标来对其内容进行索引的,而在Map中我们是通过对象对对象进行索引的,用来索引的对象叫做key,其对应的对象叫做value.这就是我们平常说的键值对. ...
- storm的并发和消息保障性
Storm并发配置的优先级: defaults.yaml < storm.yaml < topology-specific configuration < internal com ...
- 使用Ajax与服务器端通信
Ajax这个词,不代表任何东西,它仅仅是称呼一系列促进客户端与服务器通信的技术时所用的一个术语.服务器通信时Ajax技术的核心内容,其目标就是从客户端向服务器发送信息,并接受后者的回传,以求在此过程中 ...