跨线程的HTTPContext

我需要为每个Web请求实例化一个单例对象,以便数据处理一次并在整个请求中有效,我使用HttpContext.Current.Items在HTTP请求期间共享数据,一切都很好,直到我们需要单例对象实例在多个线程中,我想到的第一件事就是将HttpContext实例传递给新线程:

 HttpContext context = HttpContext.Current; ThreadPool.QueueUserWorkItem(callback => { HttpContext.Current = context; // blah blah }); 

我不认为这是一种线程安全的方法,如此处所述 。

使用Reflector我认为HttpContext.Current.Items实际上使用CallContext在每个逻辑线程中存储对象。 所以我将单例界面更改为:

 public static SingletonType SingletonInstance { get { return CallContext.GetData(key) as SingletonType; } set { CallContext.SetData(key, value); } } 

并在启动任何新线程时简单地覆盖SingletonInstance ! 代码工作正常,但似乎在某种程度上负载很重,CallContext.GetData(key)返回null并且应用程序崩溃与空引用exception!

我在想,如果CallContext.GetData是primefaces的? 但它似乎不对,CallContext是特定于线程的数据存储,必须是primefaces的,否则我就错过了这一点!

我的另一个猜测是设置SingletonInstance(CallContext.SetData)发生在一个线程中,而CallContext.GetData在另一个线程中执行,如此处所述,但我不知道如何/为什么?

更新:

我们将每个在线用户的实例保存在服务器上的arrays中。 单例对象实际上是对表示当前用户的对象的引用。 当前用户必须是唯一的,并且在每个线程中都可用于数据库查询,日志记录,error handling等等,这是如何完成的:

 public static ApplicationUser CurrentUser { get { return CallContext.GetData("ApplicationUser") as ApplicationUser ; } set { CallContext.SetData("ApplicationUser", value); } } 

如果线程处于负载状态,ASP.NET可能会在线程之间迁移请求。 收到请求后,页面构造函数可以在一个线程上执行,而页面加载在另一个线程上。 在这个线程切换中,CallContext和ThreadStatic没有被迁移,但幸运的是HttpContext。

这可能会产生误导,因为HttpContext是调用上下文,但这在ASP.NET中有点怪癖,可能是因为为了提高性能而偷工减料。

您必须删除CallContext的依赖项并完整地使用HttpContext。

您可以在Piers7的这篇了不起的博客文章中阅读更多详细信息。

这在聊天会话期间得到了解决。

从本质上讲,它涉及长时间运行的任务,并且建议使用外部服务(Web或常规Windows服务)被认为是解决问题的最佳方案。

线程安全你的第二种方法是最好的方法。 这是单线程的线程安全版本:

 public sealed class SingletonType { #region thread-safe singletone private static object _lock = new object(); private SingletonType() { } public static SingletonType SingletonInstance { get { if (CallContext.GetData(key) == null) { lock (_lock) { if (CallContext.GetData(key) == null) CallContext.SetData(key, new SingletonType()); } } return CallContext.GetData(key) as SingletonType; } } #endregion // // // SingletoneType members // // } 

注意:使用lock { }块是关键。