C#:如何实现智能缓存

Posted

技术标签:

【中文标题】C#:如何实现智能缓存【英文标题】:C#: How to implement a smart cache 【发布时间】:2010-11-11 00:07:43 【问题描述】:

我有一些地方实现某种缓存可能有用。例如,在基于自定义字符串进行资源查找、使用反射查找属性名称或每个属性名称只有一个 PropertyChangedEventArgs 的情况下。

最后一个的简单例子:

public static class Cache

    private static Dictionary<string, PropertyChangedEventArgs> cache;
    static Cache()
    
        cache = new Dictionary<string, PropertyChangedEventArgs>();
    
    public static PropertyChangedEventArgs GetPropertyChangedEventArgs(
        string propertyName)
    
        if (cache.ContainsKey(propertyName))
            return cache[propertyName];

        return cache[propertyName] = new PropertyChangedEventArgs(propertyName);
    

但是,这会很好吗?例如,如果我们有一大堆不同的 propertyNames,那意味着我们最终会得到一个巨大的缓存,永远不会被垃圾收集或任何东西。我在想如果缓存的是更大的值,如果应用程序是一个长期运行的应用程序,这可能最终会成为一个问题......或者你怎么看?一个好的缓存应该如何实现?对于大多数用途来说,这是否足够好?有没有一些不太难理解或太复杂而无法实现的不错的缓存实现示例?

【问题讨论】:

"计算机科学中只有两个难点:缓存失效和命名。" -- Phil Karlton 这不值得一个完整的答案,但这个特定的实现在多线程环境中会失败。至少,您需要在GetPropertyChangedEventArgs() 中锁定对cache 的访问权限。只是想帮助任何出现并复制意大利面的人。 【参考方案1】:

这是一个很好的辩论,但根据您的应用,这里有一些提示:

您应该定义缓存的最大大小、如果缓存已满如何处理旧项目、制定清理策略、确定缓存中对象的生存时间、是否可以/必须保留缓存内存的其他地方,万一应用程序异常终止,...

【讨论】:

什么是清除策略? 清除算法负责从缓存中移除项目以释放​​缓存存储资源。【参考方案2】:

这是一个大问题,您需要确定问题的范围并应用正确的技术。例如,您将如何描述对象的到期时间?它们会在固定的时间间隔内变得陈旧吗?它们是否因外部事件而变得陈旧?这种情况发生的频率如何?另外,你有多少个对象?最后,生成对象需要多少钱?

如上所述,最简单的策略是直接记忆。这假设对象永不过期,并且没有太多对象会耗尽您的内存并且您认为创建这些对象的成本保证了开始使用缓存。

下一层可能是限制对象的数量,并使用隐式过期策略,例如 LRU(最近最少使用)。要做到这一点,除了字典之外,您通常会使用双向链表,并且每次访问对象时,它都会移动到列表的前面。然后,如果您需要添加一个新对象,但它超出了您的对象总数限制,您将从列表的后面删除。

接下来,您可能需要根据时间或某些外部刺激强制执行显式过期。这将要求您有某种可以调用的过期事件。

正如您所见,缓存中有很多设计,因此您需要适当地了解您的领域和工程师。我觉得你没有提供足够的细节让我讨论细节。

附:请在定义类时考虑使用泛型,以便可以存储多种类型的对象,从而允许您的缓存代码被重用。

【讨论】:

+1 提到了很多因素并提出了基本的解决方案。 我有点忽略了特殊性,只是因为我希望我能得到更多关于思​​考什么的一般性建议等等。所以谢谢你:) 在使用泛型时,你的意思是把 Cache 换成Cache 并用 T 换出所有 PropertyChangedEventArgs 吗?或者更聪明的东西?并非所有其他对象都可以通过我认为的字符串来识别... 只是基本的泛型是的,您可能希望使用与 Dictionary 相同的模式,并让缓存的用户确定 K 和 V 的类型。【参考方案3】:

您可以将每个缓存项目包装在 WeakReference 中。这将允许 GC 在需要时回收项目,但它不会让您对项目何时从缓存中消失进行任何精细控制,或者允许您实施明确的过期策略等。

(哈!我刚刚注意到MSDN page 上给出的示例是一个简单的缓存类。)

【讨论】:

酷!在这种情况下,WeakReference 究竟做了什么? 这个页面有很好的解释:msdn.microsoft.com/en-us/library/ms404247.aspx 在实践中,我发现 .NET GC 非常高效/激进,以至于弱引用不会长时间存在,这意味着它们仅适用于短暂的数据和/或重建成本低廉。 是的,我没有明确建议 WeakReference,因为您无法控制对象何时过期。因此,只有当它们占用大量内存并且制造成本低廉时才会有好处——这似乎与大多数缓存相反。【参考方案4】:

这是一个常见问题,根据您的应用需求有多种解决方案。 这很常见,以至于微软发布了一个完整的库来解决它。 在汇总您自己的缓存之前,您应该查看 Microsoft Velocity。 http://msdn.microsoft.com/en-us/data/cc655792.aspx 希望对您有所帮助。

【讨论】:

+1 - 这个库看起来像是源于企业库中的缓存应用程序块。 现在称为“AppFabric 缓存服务”。这里缺少两个关键词:可移植性和免费。为什么微软不能简单地发布一些东西作为 .NET 的标准部分?【参考方案5】:

您可以使用WeakReference,但如果您的对象不是那么大,则不要这样做,因为WeakReference 会比对象本身占用更多的内存,这不是一个好的技术。此外,如果对象是短期使用,它永远不会在 GC 上从第 0 代进入第 1 代,则对 WeakReference 的需求不大,但对象上的 IDisposable 接口将在 @ 上发布987654324@.

如果您想控制生命周期,您需要一个计时器来再次更新缓存中对象的desiredExpirationTime。

重要的是如果对象很大,那么选择弱引用,否则使用强引用。此外,您可以在 Dictionary 上设置容量,并创建一个队列以请求临时 bin 中的其他对象,序列化对象并在 Dictionary 中有空间时加载它,然后从 temp 目录中清除它。

【讨论】:

【参考方案6】:

看起来 .NET 4.0 现在支持 System.Runtime.Caching 来缓存多种类型的东西。你应该先研究一下,而不是重新发明***。更多详情:

http://msdn.microsoft.com/en-us/library/system.runtime.caching%28VS.100%29.aspx

【讨论】:

除了它使用String作为key,Object作为map的值!伙计,真令人失望,他们如何实现像缓存这样的通用 API 而不使用泛型作为键/对象类型? 嗯,我可以理解使用字符串作为键。我不知道有任何散列对象至少不会将键转换为基于字符串的东西。 Object 不只是一个通用容器吗? @marq 您需要一个对象作为缓存键的真实世界用例是什么?您是否将某些键命名为 1234 而不是“1234”,我的意思是没有什么可以证明实现这一点的合理性。这是一个“噪音”功能。

以上是关于C#:如何实现智能缓存的主要内容,如果未能解决你的问题,请参考以下文章

C语言实现LRU缓存策略

在 C# 中清除智能令牌的 Pin 缓存

LRU缓存替换策略及C#实现

C# 实现 key-value 结构自定义缓存 CustomCache

基于C#实现与新大陆扫码枪通信

C#实现DNS解析服务和智能DNS服务