单例是否自动保持在ASP.NET MVC中的请求之间?

时间:2010-08-12 00:19:54

标签: asp.net-mvc-2 object-lifetime

我有一个包含数千个整数的查找表(LUT),我根据从数据库中提取的内容计算了大量的请求。

如果我只是创建一个标准单例来保存LUT,它是否会在请求之间自动保留,还是我需要将其推送到应用程序状态?

如果它们是自动保留的,那么将它们与Application状态存储有什么区别?

正确的单例实现如何?它不需要懒惰地初始化,但它需要是线程安全的(每个服务器实例有数千个理论用户)并且具有良好的性能。

编辑:Jon Skeet的第四个版本看起来很有希望http://csharpindepth.com/Articles/General/Singleton.aspx

public sealed class Singleton
{
    static readonly Singleton instance=new Singleton();

    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Singleton()
    {
    }

    Singleton()
    {
    }

    public static Singleton Instance
    {
        get
        {
            return instance;
        }
    }

    // randomguy's specific stuff. Does this look good to you?
    private int[] lut = new int[5000];

    public int Compute(Product p) {
        return lut[p.Goo];
    }
}

3 个答案:

答案 0 :(得分:2)

我不会依赖于请求之间保持静态。 [在请求之间重置进程的过程总是存在,尽管不太可能。]我建议使用HttpContext的Cache对象来保持请求之间的共享资源。

答案 1 :(得分:2)

是的,静态成员仍然存在(与持久性不一样 - 它不是“保存”,它永远不会消失),其中包括单例的实现。您可以免费获得一定程度的延迟初始化,就好像它是在静态赋值或静态构造函数中创建的,在首次使用相关类之前不会调用它。该创建默认锁定,但所有其他用途必须是线程安全的,如你所说。鉴于所涉及的并发程度,那么除非单例是不可变的(你的查找表在应用程序生命周期内没有改变)你必须非常小心你如何更新它(一种方式是假单身人士) - 在更新时,你创建一个新对象,然后锁定分配它来替换当前值;虽然它看起来像一个“从外面”,但不是严格的单例。)

最大的危险是引入全球状态的任何事情都是可疑的,特别是在处理像网络这样的无状态协议时。它可以很好地使用,特别是作为永久或接近永久数据的内存缓存,特别是如果它涉及一个无法从数据库中快速获得的对象图。

但是陷阱很大,所以要小心。特别是,锁定问题的风险不容低估。

编辑,以匹配问题中的修改:

我最担心的是阵列的初始化方式。很明显,这个例子是不完整的,因为每个项目只有0。如果它设置为初始化并且是只读的,那么很好。如果它是可变的,那么对你的线程非常非常小心。

还要注意太多此类查找对缩放的负面影响。虽然您在预先计算中保存了大多数请求,但是当更新单例时,效果是有一段非常繁重的工作。长期启动可能是可以容忍的(因为它不会经常发生),但随后发生的任意减速可能很难追查其来源。

答案 2 :(得分:0)

编辑:请参阅Jon关于只读锁定的评论。

我已经有一段时间了,因为我已经处理过单例(我更喜欢让我的IOC容器处理生命周期),但是这里是你如何处理线程安全问题。你需要锁定任何改变单身人士状态的东西。只读操作,例如Compute(int)不需要锁定。

// I typically create one lock per collection, but you really need one per set of atomic operations; if you ever modify two collections together, use one lock.
private object lutLock = new object();
private int[] lut = new int[5000];

public int Compute(Product p) {
    return lut[p.Goo];
}

public void SetValue(int index, int value)
{
    //lock as little code as possible. since this step is read only we don't lock it.
    if(index < 0 || index > lut.Length)
    {
        throw new ArgumentException("Index not in range", "index");
    }
    // going to mutate state so we need a lock now
    lock(lutLock)
    {
        lut[index] = value;
    }
}