【问题标题】:Using an HTTPContext across threads跨线程使用 HTTPContext
【发布时间】:2009-04-09 15:51:30
【问题描述】:

用户点击页面 spawn.aspx,然后生成六个线程,所有页面都使用

 ((System.Web.IHttpHandler)instance).ProcessRequest(reference to spawn's HTTPContext);

不用担心 ASP.Net 似乎为 1 个请求向用户发送了 7 个响应,该部分已处理并且只发送一个响应。

问题是,在具有许多线程(quad-quads)的高流量环境(我们的生产环境)中,我们会收到错误:

System.IndexOutOfRangeException 
at System.collections.ArrayList.Add 
at System.Web.ResponseDependencyList.AddDependencies(String[] items, String argname, Boolean cloneArray, DateTime utcDepTime) 
at System.Web.ResponseDependencyList.AddDependencies(String[] items, String argname, Boolean cloneArray, String requestVritualPath)
at System.Web.UI.Page.AddWrappedFileDependencies(Object virtualFileDependencies) 
at ASP.spawned_page_no_1_aspx.FrameworkInitialize()
at System.Web.UI.Page.ProcessRequest

我们不能在其他地方复制它。我的同事认为这是因为我重用了原始 HTTPContext 并将其传递给其他线程,并且它不是线程安全的。

按照这个逻辑,我尝试制作一个新的 HTTPContext 来传递给线程。但它的某些部分似乎不会“合并”。具体来说,我需要将 Session 对象放入新的 HTTPContext。我想我也想加入其他部分,比如缓存。记录一下 HTTPContext.Current.Session.IsSynchronized 是假的。

我的问题是:

  1. 您认为错误是由于跨线程使用 HTTPContext 造成的吗?
  2. 我该如何解决?
  3. 如果修复是为每个线程复制 HTTPContext,我如何才能将会话(和缓存)放入新的? Request 和 Response 进入 ctor,但 Session 不可设置。

编辑:更多详情

所以回到这句话:“不要担心 ASP.Net 似乎为 1 个请求向用户发送了 7 个响应,该部分被处理并且只发送一个响应。” Raymond Chen 的超级粉丝,我同意你的观点:“现在你有两个问题”在没有更多信息的情况下是一个合理的说法。

实际发生的是,我正在构建一个 Excel 文档以发回。在 spawn.aspx 页面中,它设置了一些状态信息,包括它正在渲染到 excel 的事实,以及要渲染到的对象。每个生成的页面都会获取该信息,并且会一直阻塞,直到轮到它们呈现给对象。如果字面上看起来像这样:

 protected override void Render(System.Web.UI.HtmlTextWriter writer)
 {
    if (this.RenderToExcel)
    {
      Deadlocker.SpinUntilCurrent(DeadLockToken);
      RenderReport(this, this.XLSWriter);
      Deadlocker.Remove(DeadLockToken);
    }
    else
      base.Render(writer);
 }

但到那时为止的所有处理 - 数据库访问、控制层次结构,所有这些都是并行完成的。而且还有很多 - 足以将它并行化,同时仍然让它在 Render 上阻塞,将总时间减少一半以上。

其中最好的部分是 - 无需为 Excel 渲染重写任何内容。所有控件都知道如何将自己呈现为 excel,并且您可以独立访问每个生成的页面(这实际上是“正常情况” - excel 报告只是所有生成的页面的聚合。)

所以我认为最终结果将是“你不能这样做,你需要重新考虑方法”——但我至少必须尝试一下,因为事实上一切都运行得很好,没有重复任何逻辑或任何代码或必须抽象任何东西都是如此完美。问题只是多线程,如果我连续渲染页面一切都很好,只是速度很慢。

【问题讨论】:

    标签: asp.net multithreading httpcontext


    【解决方案1】:

    虽然 HttpContext 旨在处理非线程特定的上下文(因为 http 上下文可以在一个线程上开始并在另一个线程上结束),但它并不是隐式线程安全的。

    本质上问题是您正在做一些非预期的事情,这些请求通常是多个请求,每个请求都有自己分配的 HttpApplication 来完成请求,并且每个请求都有自己的 HttpContext。

    我真的会尝试让 asp.net 基础架构自己委托请求。

    【讨论】:

      【解决方案2】:

      你的同事是对的,如果一个线程锁定了一个资源而另一个线程试图使用它,那么你的线程池就会繁荣起来!结果不是很好。大多数人通过创建新对象并将它们传递给参数化线程来解决这个问题。如果你绝对需要使用同一个对象,那么你必须实现一些代码,首先检查资源是否被另一个线程使用,然后等待一段时间再检查。一个示例是创建一个您始终首先检查的 IsInUse bool,然后如果它正在使用该资源,则您的线程将其设置为 true,然后在完成时将其设置为 false,以防止其他线程尝试使用底层资源(您的 httpContext) .我希望这有帮助。

      【讨论】:

      • Al 这种锁定可能很难安排,他得到的线程异常来自 Page 类,它正在改变 http 上下文,除非他可以覆盖执行此操作的页面中的操作,并且放置一个锁,然后锁定解决方案将不起作用。
      • 很好的评论,我同意你的看法。我的偏好是传递一个全新的对象,该对象在创建踏板时从 HTTP 上下文中派生一些信息。那将是防弹的。
      【解决方案3】:

      由于 HttpContext 是 .Net 库的一部分,我希望所有静态函数都是线程安全的,但在针对对象的同一实例调用时,任何非静态成员都不是线程安全的。所以希望下次你会预料到跨线程共享 HttpContext 实例会有问题。

      有没有一种方法可以将需要并行执行的操作与 HttpContext 分离?如果他们只是加载数据并写入 CSV 格式,那么该代码对 ASP.NET 用户控件或页面生命周期没有必要的依赖。移除该依赖后,您可以使用异步 HttpHandler 实现页面,在 IHttpHandler.BeginProcessingRequest() 和 IHttpHandler.EndProcessingRequest() 之间运行并行操作。

      【讨论】:

        【解决方案4】:

        我会确保在您访问 HttpContext.Current.Items 集合时,您使用 HttpContext.Current.Items.SyncRoot 对象上的监视器锁 (C#)/SyncLock(VB) 块来包装您的调用。

        【讨论】:

          猜你喜欢
          • 2012-04-14
          • 2016-01-30
          • 1970-01-01
          • 1970-01-01
          • 2011-01-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多