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

发布于 2024-09-13 14:04:51 字数 984 浏览 1 评论 0原文

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

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

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

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

编辑: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];
    }
}

I have a lookup table (LUT) of thousands integers that I use on a fair amount of requests to compute stuff based on what was fetched from database.

If I simply create a standard singleton to hold the LUT, is it automatically persisted between requests or do I specifically need to push it to the Application state?

If they are automatically persisted, then what is the difference storing them with the Application state?

How would a correct singleton implementation look like? It doesn't need to be lazily initialized, but it needs to be thread-safe (thousands of theoretical users per server instance) and have good performance.

EDIT: Jon Skeet's 4th version looks promising 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];
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

稳稳的幸福 2024-09-20 14:04:52

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

I wouldn't rely on a static being persisted between requests. [There is always the, albeit unlikely, chance that the process would be reset between requests.] I'd recommend HttpContext's Cache object for persisting shared resources between requests.

没企图 2024-09-20 14:04:52

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

自从我处理单例以来已经有一段时间了(我更喜欢让我的 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;
    }
}

Edit: See Jon's comments about read-only locking.

It's been a while since I've dealt with singleton's (I prefer letting my IOC container deal with lifetimes), but here's how you can handle the thread-safety issues. You'll need to lock around anything that mutates the state of the singleton. Read only operations, like your Compute(int) won't need locking.

// 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;
    }
}
身边 2024-09-20 14:04:51

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

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

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

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

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

还要注意太多此类查找对缩放的负面影响。虽然您在预先计算中节省了大多数请求,但结果是在更新单例时需要一段非常繁重的工作。较长的启动时间可能是可以忍受的(因为这种情况不会经常发生),但随后发生的任意减速可能很难追踪到其根源。

Yes, static members persists (not the same thing as persisted - it's not "saved", it never goes away), which would include implementations of a singleton. You get a degree of lazy initialisation for free, as if it's created in a static assignment or static constructor, it won't be called until the relevant class is first used. That creation locks by default, but all other uses would have to be threadsafe as you say. Given the degree of concurrency involved, then unless the singleton was going to be immutable (your look-up table doesn't change for application lifetime) you would have to be very careful as to how you update it (one way is a fake singleton - on update you create a new object and then lock around assigning it to replace the current value; not strictly a singleton though it looks like one "from the outside").

The big danger is that anything introducing global state is suspect, and especially when dealing with a stateless protocol like the web. It can be used well though, especially as an in-memory cache of permanent or near-permanent data, particularly if it involves an object graph that cannot be easily obtained quickly from a database.

The pitfalls are considerable though, so be careful. In particular, the risk of locking issues cannot be understated.

Edit, to match the edit in the question:

My big concern would be how the array gets initialised. Clearly this example is incomplete as it'll only ever have 0 for each item. If it gets set at initialisation and is the read-only, then fine. If it's mutable, then be very, very careful about your threading.

Also be aware of the negative effect of too many such look-ups on scaling. While you save for mosts requests in having pre-calculation, the effect is to have a period of very heavy work when the singleton is updated. A long-ish start-up will likely be tolerable (as it won't be very often), but arbitrary slow downs happening afterwards can be tricky to trace to their source.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文