“HttpContext.Current”属性和相关事物的交叉线程使用

我从<使用C#中的示例的基本ASP.NET>读取以下语句:

另一个有用的属性是HttpContext类的静态Current属性。 此属性始终指向正在服务的请求的HttpContext类的当前实例。 如果您正在编写将从页面或其他pipe道类使用的辅助类,并且可能因任何原因需要访问上下文,这可能会很方便。 通过使用静态Current属性来检索上下文,可以避免将引用传递给helper类。 例如,清单4-1中所示的类使用上下文的Current属性来访问QueryString并将某些内容打印到当前的响应缓冲区中。 请注意,为了正确初始化此静态属性,调用者必须在原始请求线程上执行,因此如果您在请求期间生成了其他线程以执行工作,则必须注意自己提供对上下文类的访问权限。

我想知道大胆的部分的根源,有一件事导致另一件事,这是我的想法:

我们知道一个进程可以有多个线程。 每个线程都有自己的堆栈。 这些线程也可以访问共享内存区域,即堆。

据我所知,堆栈就是存储该线程所有上下文的地方。 对于一个线程来访问堆中的东西,它必须使用一个指针,并将指针存储在堆栈中。

所以当我们进行一些跨线程调用时,我们必须确保所有必要的上下文信息都从调用者线程的堆栈传递到被调用者线程的堆栈。

但是我不太清楚,如果我犯了什么错误。

任何意见将深表赞赏。

谢谢。

这里的堆栈被限制在用户堆栈中

有四件事情共同导致你所问的行为:

  1. HttpContext是一个实例对象,它的引用可以在HttpContext.Currentfind
  2. 线程也是一个实例对象,它的引用可以在Thread.CurrentThreadfind
  3. Thread.CurrentThread是静态的,但在每个线程中引用了不同的Thread对象
  4. HttpContext.Current实际上指向Thread.CurrentThread.ExecutionContext.IllogicalCallContext.HostContext

我们可以从上面的结论中得出结论:

  1. 由于HttpContext是一个实例对象,而不是静态的,我们需要它的引用来访问它
  2. 由于HttpContext.Current实际上指向Thread.CurrentThread上的一个属性,因此将Thread.CurrentThread更改为其他对象可能会更改HttpContext.Current
  3. 因为Thread.CurrentThread在切换线程时发生变化,所以在切换线程时HttpContext.Current也会改变(在这种情况下, HttpContext.Current变为null)。

综合起来,是什么导致HttpContext.Current无法在新的线程中工作? 线程切换时发生的Thread.CurrentThread引用更改会更改HttpContext.Current引用,从而阻止我们获取到所需的HttpContext实例。

重申一下,这里唯一不可思议的事情是Thread.CurrentThread在每个Thread中引用一个不同的对象。 HttpContext与其他实例对象一样工作。 由于同一AppDomain中的线程可以引用相同的对象,我们所要做的就是将HttpContext的引用传递给我们的新线程。 没有上下文信息加载或类似的东西。 (有一些相当严重的潜在问题,将HttpContext传递给其他线程,但没有任何阻止你这样做)。

在研究过程中,我遇到了一些最后的附注:

  1. 在某些情况下,一个线程的ExecutionContext从一个线程“stream动”(复制)到另一个线程。 为什么HttpContext不能“stream”到我们的新线程呢? 因为HttpContext没有实现ILogicalThreadAffinative接口。 存储在ExecutionContext中的类只有在实现ILogicalThreadAffinative时才会stream动。

  2. ASP.NET如何将HttpContext从线程移动到线程(线程敏捷),如果它没有stream动? 我不完全确定,但它看起来可能会通过HttpApplication.OnThreadEnter()

我想我在这里find了一个合适的解释: http : //odetocode.com/articles/112.aspx

总而言之, HttpContext.Current的代码隐藏如下所示:

 public static HttpContext get_Current() { return (CallContext.GetData("HtCt") as HttpContext); } 

使用CallContext作为线程本地存储 (即每个线程将看到不同的数据副本,并且不能访问其他线程看到的副本)。 因此,一旦当前上下文在一个线程上被初始化,其他线程的后续访问将导致NullReferenceException ,因为该属性对于初始线程是线程本地的。

所以是的,你最初的解释是非常接近的,从某种意义上说,这是一个数据只能被一个线程看到的问题。

Current的后台字段被标记为ThreadStatic (我假设),因此它不会在用户创build的线程中可用/初始化。

最重要的是,你应该在请求线程中捕获HttpContext.Current的实例,然后在线程中使用该实例,而不是引用HttpContext.Current