Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/api/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 跨线程的HTTPContext_C#_Asp.net_Multithreading_Thread Safety_Singleton - Fatal编程技术网

C# 跨线程的HTTPContext

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

我需要为每个web请求实例化一个单例对象,以便数据处理一次并且在整个请求过程中有效,我使用了
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
来获得一个免费的数据槽。所以这里没有并发,也不需要锁定任何猜测的东西。但为什么要打电话