C# 跨线程的HTTPContext
我需要为每个web请求实例化一个单例对象,以便数据处理一次并且在整个请求过程中有效,我使用了C# 跨线程的HTTPContext,c#,asp.net,multithreading,thread-safety,singleton,C#,Asp.net,Multithreading,Thread Safety,Singleton,我需要为每个web请求实例化一个单例对象,以便数据处理一次并且在整个请求过程中有效,我使用了HttpContext.Current.Items在HTTP请求期间共享数据,在我们需要跨多个线程的单例对象实例之前,一切都很好,我想到的第一件事是将HttpContext实例传递给新线程: HttpContext context = HttpContext.Current; ThreadPool.QueueUserWorkItem(callback => { HttpCon
HttpContext.Current.Items
在HTTP请求期间共享数据,在我们需要跨多个线程的单例对象实例之前,一切都很好,我想到的第一件事是将HttpContext实例传递给新线程:
HttpContext context = HttpContext.Current;
ThreadPool.QueueUserWorkItem(callback =>
{
HttpContext.Current = context;
// blah blah
});
我认为这不是一种线程安全的方法
使用Reflector,我认为HttpContext.Current.Items实际上使用CallContext在每个逻辑线程中存储对象。因此,我将singleton接口更改为:
public static SingletonType SingletonInstance
{
get { return CallContext.GetData(key) as SingletonType; }
set { CallContext.SetData(key, value); }
}
启动任何新线程时,只需覆盖singletonistance
!代码运行良好,但似乎在重载情况下,CallContext.GetData(key)返回null,应用程序崩溃,出现null引用异常
我在想,如果CallContext.GetData
是原子的?但它似乎并不正确,CallContext是线程特定的数据存储,必须是原子的,否则我就没有抓住要点
我的另一个猜测是设置singletonistance(CallContext.SetData)发生在一个线程中,而CallContext.GetData在另一个线程中执行,但我不知道如何/为什么
更新:
我们在服务器上的一个数组中保留每个在线用户的一个实例。singleton对象实际上是对表示当前用户的对象的引用。当前用户必须是唯一的,并且在每个线程中都可用于数据库查询、日志记录、错误处理等,具体操作如下:
public static ApplicationUser CurrentUser
{
get { return CallContext.GetData("ApplicationUser") as ApplicationUser ; }
set { CallContext.SetData("ApplicationUser", value); }
}
这是在聊天过程中解决的
本质上,它涉及到长时间运行的任务,使用外部服务(Web或常规Windows服务)的建议被认为是解决该问题的最佳解决方案。ASP.NET可能会在线程之间迁移负载不足的请求。一旦收到请求,页面构造函数就可以在一个线程上执行,在另一个线程上加载页面。在此线程开关中,不会迁移CallContext和ThreadStatic,但luckaly HttpContext会迁移 这可能会产生误导,因为HttpContext是调用上下文,但这在ASP.NET中有点奇怪,可能是因为为了提高性能而偷工减料 您必须删除CallContext的依赖项,并在整个过程中使用HttpContext
您可以阅读Piers7提供的更多详细信息。使用第二种方法保护线程是最好的方法。 这是单音的线程安全版本:
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{}
块是关键。HttpContext指的是单个Http请求。在线程中的某个位置将其排队将导致一些问题(您显然已经发现)。也许如果你描述一下你想做什么,我们可以想出一个更好的解决方案?问题很简单,我需要在我的应用程序中有一个单一的对象引用,它可以通过任何请求实例化(可能是应用程序BeginRequest),并且在整个请求过程中都是活动的,在应用程序的整个生命周期中,缓存或应用程序都是存储数据的一般位置。我需要一个位置来存储每个请求的信息,这可以使用HttpContext.Current.Items来完成。然而,正如我上面提到的,这不能用多线程应用程序来完成@Kamyanazeri,见我的最新答案。在请求开始时实例化它,并在请求结束时销毁它。它是线程安全的。如果请求在另一个线程/线程完成工作之前结束怎么办?在这种情况下,我需要跟踪每个线程,并在最后一个线程结束时以某种方式处理缓存!另一个请求可能会命中应用程序,并将错误数据写入缓存!当然,为不同的用户/请求提供不同的密钥是另一个需要特别注意的问题@KamyarNazeri,也许你可以扩展一下你实际上在做什么来实现这一点?记住HttpContext。当前值在线程之间为null,因此会话不能是存储数据的正确位置感谢Nikola,这基本上解释了一切,但是HttpContext。当前值在线程之间恰好为null,我在考虑同时使用HttpContext和CallContext,HttpContext用于从BeginRequest到请求结束的reular ASP.net请求,以及CallContext,无论何时启动新线程,您认为如何?我没有看到太多选项。您要么需要1)在将方法排队到ThreadPool时将所需的所有数据传递给方法,要么2)实际发送HttpContext并小心处理。您确实链接到了一个SO问题,该问题指出这“不是线程安全的”。此操作是线程安全的,您将发送正确的HttpContext实例,但它所包含的集合不是线程安全的,因此您需要注意如何访问它。为了安全起见,也许您可以避免将其分配给HttpContext.Current工作线程,并直接使用它,或者至少在工作线程完成后清理它。这是完全错误的!锁定不适用于此处。CallContext方法都是静态的,并且在当前线程中的调用上下文上操作,这意味着它们在数据槽上工作,这些数据槽对于执行的每个逻辑线程都是唯一的,并且它们本身是线程安全的!我认为CallContext返回的空值是因为两个线程同时设置了CallContext键。而且[可能]CallContext.SetData()中出现了一些错误。。。因此,锁定将管理它。而且它没有性能问题。你可以测试。也许空值会消失。顺便说一句,我没有亲自测试。锁定不会锁定字段、属性或其他内容。它锁定一段代码。我只是认为它是第二种方法的更好实现。如果你看一下.net源代码,你会注意到SetData
方法实际上利用Thread.CurrentThread
来获得一个免费的数据槽。所以这里没有并发,也不需要锁定任何猜测的东西。但为什么要打电话