跨线程的HTTPContext

本文关键字:HTTPContext 线程 | 更新日期: 2023-09-27 18:29:40

我需要为每个web请求实例化一个singleton对象,这样数据就可以处理一次,并且在整个请求过程中都是有效的,我在HTTP请求期间使用HttpContext.Current.Items共享数据,一切都很好,直到我们需要跨多个线程的singleton对象实例,我想到的第一件事是将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); }
}

在启动任何新线程时,只需覆盖SingletonInstance!代码运行良好,但似乎在重载情况下,CallContext.GetData(键)返回null,应用程序崩溃,出现null引用异常!

我在想,如果CallContext.GetData是原子的?但这似乎并不正确,CallContext是线程特定的数据存储,必须是原子的,否则我就没有抓住要点!

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

更新:

我们在服务器上的一个数组中保留每个在线用户的一个实例。singleton对象实际上是对表示当前用户的对象的引用。当前用户必须是唯一的,并且在每个线程中都可以用于数据库查询、日志记录、错误处理等,这就是它的实现方式:

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

跨线程的HTTPContext

ASP.NET可能会在负载不足的情况下在线程之间迁移请求。一旦接收到请求,页面构造函数就可以在一个线程上执行,并在另一个线程中加载页面。在这个线程切换中,CallContext和ThreadStatic没有被迁移,但luckaly HttpContext是.

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

您必须删除对CallContext的依赖关系,并全程使用HttpContext。

你可以在Piers7的这篇精彩的博客文章中阅读更多细节。

这是在聊天会话中解决的。

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

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

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 { }块是关键。