跨线程的HTTPContext

时间:2012-03-31 16:05:07

标签: c# asp.net multithreading thread-safety singleton

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

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

我不认为这是一种线程安全的方法noted here

使用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,应用程序崩溃与空引用异常!

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

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

更新

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

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

3 个答案:

答案 0 :(得分:3)

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

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

你必须删除对CallContext的依赖,并通过整个方式使用HttpContext。

您可以通过Piers7在此terrific blog post中阅读更多详细信息。

答案 1 :(得分:1)

在聊天会话期间解决了这个问题。

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

答案 2 :(得分:0)

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

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