3

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

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

如果它们是自动持久化的,那么将它们与应用程序状态一起存储有什么区别?

正确的单例实现会是什么样子?它不需要延迟初始化,但它需要是线程安全的(每个服务器实例有数千个理论用户)并具有良好的性能。

编辑:Jon Skeet 的第 4 版看起来很有希望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];
    }
}
4

3 回答 3

2

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

于 2010-08-12T00:25:56.263 回答
2

是的,静态成员持续存在(与持续存在不同 - 它不是“保存”,它永远不会消失),其中包括单例的实现。您可以免费获得一定程度的惰性初始化,就好像它是在静态赋值或静态构造函数中创建的一样,在首次使用相关类之前不会调用它。默认情况下创建锁定,但所有其他用途都必须是线程安全的,正如你所说。考虑到所涉及的并发程度,除非单例是不可变的(您的查找表在应用程序生命周期内不会改变),否则您必须非常小心如何更新它(一种方法是假单例- 在更新时,您创建一个新对象,然后锁定分配它以替换当前值;虽然它看起来像一个,但并不是严格意义上的单例“

最大的危险是任何引入全局状态的东西都是可疑的,尤其是在处理像 web 这样的无状态协议时。但它可以很好地使用,特别是作为永久或接近永久数据的内存缓存,特别是当它涉及无法从数据库中快速获取的对象图时。

陷阱是相当大的,所以要小心。特别是,不能低估锁定问题的风险。

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

我最关心的是如何初始化数组。显然,这个例子是不完整的,因为每个项目只有 0。如果它在初始化时被设置并且是只读的,那么很好。如果它是可变的,那么对你的线程非常非常小心。

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

于 2010-08-12T01:10:54.263 回答
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;
    }
}
于 2010-08-12T03:39:39.640 回答