zoukankan      html  css  js  c++  java
  • 我所知道的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对象会被关联创建,所以,全局缓存他是没必要的。

  • 相关阅读:
    textarea聚焦的多种写法
    vue.js入门
    全选,反选
    jquery列表,点击反应
    SqlServer中offset..fetch 的使用问题
    复习Spring第四课---Spring对国际化的支持
    SqlServer的order by问题
    设计模式之---代理模式
    解决SpringMVC重复提交的问题
    Java知识复习(三)
  • 原文地址:https://www.cnblogs.com/scy251147/p/3549503.html
Copyright © 2011-2022 走看看