C#:如何实现智能缓存

时间:2022-04-06 18:04:36

I have some places where implementing some sort of cache might be useful. For example in cases of doing resource lookups based on custom strings, finding names of properties using reflection, or to have only one PropertyChangedEventArgs per property name.

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

A simple example of the last one:

最后一个简单的例子:

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);
    }
}

But, will this work well? For example if we had a whole load of different propertyNames, that would mean we would end up with a huge cache sitting there never being garbage collected or anything. I'm imagining if what is cached are larger values and if the application is a long-running one, this might end up as kind of a problem... or what do you think? How should a good cache be implemented? Is this one good enough for most purposes? Any examples of some nice cache implementations that are not too hard to understand or way too complex to implement?

但是,这会有效吗?例如,如果我们有一大堆不同的propertyNames,那就意味着我们最终会有一个巨大的缓存,从来没有被垃圾收集或任何东西。我在想如果缓存的内容是更大的值,如果应用程序是一个长期运行的应用程序,这可能最终会成为一个问题......或者你怎么看?如何实现好的缓存?这个对大多数用途来说是否足够好?一些不太好理解的高级缓存实现的例子,或者实现起来太复杂了?

6 个解决方案

#1


You could wrap each of your cached items in a WeakReference. This would allow the GC to reclaim items if-and-when required, however it doesn't give you any granular control of when items will disappear from the cache, or allow you to implement explicit expiration policies etc.

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

(Ha! I just noticed that the example given on the MSDN page is a simple caching class.)

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

#2


This is a large problem, you need to determine the domain of the problem and apply the correct techniques. For instance, how would you describe the expiration of the objects? Do they become stale over a fixed interval of time? Do they become stale from an external event? How frequently does this happen? Additionally, how many objects do you have? Finally, how much does it cost to generate the object?

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

The simplest strategy would be to do straight memoization, as you have above. This assumes that objects never expire, and that there are not so many as to run your memory dry and that you think the cost to create these objects warrants the use of a cache to begin with.

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

The next layer might be to limit the number of objects, and use an implicit expiration policy, such as LRU (least recently used). To do this you'd typically use a doubly linked list in addition to your dictionary, and every time an objects is accessed it is moved to the front of the list. Then, if you need to add a new object, but it is over your limit of total objects, you'd remove from the back of the list.

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

Next, you might need to enforce explicit expiration, either based on time, or some external stimulus. This would require you to have some sort of expiration event that could be called.

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

As you can see there is alot of design in caching, so you need to understand your domain and engineer appropriately. You did not provide enough detail for me to discuss specifics, I felt.

正如您所看到的,缓存中有很多设计,因此您需要正确理解您的域和工程师。我觉得你没有提供足够的细节来讨论具体细节。

P.S. Please consider using Generics when defining your class so that many types of objects can be stored, thus allowing your caching code to be reused.

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

#3


Looks like .NET 4.0 now supports System.Runtime.Caching for caching many types of things. You should look into that first, instead of re-inventing the wheel. More details:

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

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

#4


This is a nice debate to have, but depending your application, here's some tips:

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

You should define the max size of the cache, what to do with old items if your cache is full, have a scavenging strategy, determine a time to live of the object in the cache, does your cache can/must be persisted somewhere else that memory, in case of application abnormal termination, ...

您应该定义缓存的最大大小,如果缓存已满,如何处理旧项目,具有清理策略,确定缓存中对象的生存时间,缓存是否/必须在其他位置保留缓存内存,如果应用程序异常终止,...

#5


This is a common problem that has many solutions depending on your application need. It is so common that Microsoft released a whole library to address it. You should check out Microsoft Velocity before rolling up your own cache. http://msdn.microsoft.com/en-us/data/cc655792.aspx Hope this help.

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

#6


You could use a WeakReference but if your object is not that large than don't because the WeakReference would be taking more memory than the object itself which is not a good technique. Also, if the object is a short-time usage where it will never make it to generation 1 from generation 0 on the GC, there is not much need for the WeakReference but IDisposable interface on the object would have with the release on SuppressFinalize.

您可以使用WeakReference但是如果您的对象不是那么大,因为WeakReference将占用比对象本身更多的内存,这不是一个好技术。此外,如果对象是短时间使用,它将永远不会从GC的第0代进入第1代,则不需要WeakReference,但对象上的IDisposable接口将与SuppressFinalize上的版本一起使用。

If you want to control the lifetime you need a timer to update the datetime/ timespan again the desiredExpirationTime on the object in your cache.

如果要控制生命周期,则需要一个计时器来再次更新日期时间/时间跨度缓存中对象上的desiredExpirationTime。

The important thing is if the object is large then opt for the WeakReference else use the strong reference. Also, you can set the capacity on the Dictionary and create a queue for requesting additional objects in your temp bin serializing the object and loading it when there is room in the Dictionary, then clear it from the temp directory.

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

#1


You could wrap each of your cached items in a WeakReference. This would allow the GC to reclaim items if-and-when required, however it doesn't give you any granular control of when items will disappear from the cache, or allow you to implement explicit expiration policies etc.

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

(Ha! I just noticed that the example given on the MSDN page is a simple caching class.)

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

#2


This is a large problem, you need to determine the domain of the problem and apply the correct techniques. For instance, how would you describe the expiration of the objects? Do they become stale over a fixed interval of time? Do they become stale from an external event? How frequently does this happen? Additionally, how many objects do you have? Finally, how much does it cost to generate the object?

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

The simplest strategy would be to do straight memoization, as you have above. This assumes that objects never expire, and that there are not so many as to run your memory dry and that you think the cost to create these objects warrants the use of a cache to begin with.

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

The next layer might be to limit the number of objects, and use an implicit expiration policy, such as LRU (least recently used). To do this you'd typically use a doubly linked list in addition to your dictionary, and every time an objects is accessed it is moved to the front of the list. Then, if you need to add a new object, but it is over your limit of total objects, you'd remove from the back of the list.

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

Next, you might need to enforce explicit expiration, either based on time, or some external stimulus. This would require you to have some sort of expiration event that could be called.

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

As you can see there is alot of design in caching, so you need to understand your domain and engineer appropriately. You did not provide enough detail for me to discuss specifics, I felt.

正如您所看到的,缓存中有很多设计,因此您需要正确理解您的域和工程师。我觉得你没有提供足够的细节来讨论具体细节。

P.S. Please consider using Generics when defining your class so that many types of objects can be stored, thus allowing your caching code to be reused.

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

#3


Looks like .NET 4.0 now supports System.Runtime.Caching for caching many types of things. You should look into that first, instead of re-inventing the wheel. More details:

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

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

#4


This is a nice debate to have, but depending your application, here's some tips:

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

You should define the max size of the cache, what to do with old items if your cache is full, have a scavenging strategy, determine a time to live of the object in the cache, does your cache can/must be persisted somewhere else that memory, in case of application abnormal termination, ...

您应该定义缓存的最大大小,如果缓存已满,如何处理旧项目,具有清理策略,确定缓存中对象的生存时间,缓存是否/必须在其他位置保留缓存内存,如果应用程序异常终止,...

#5


This is a common problem that has many solutions depending on your application need. It is so common that Microsoft released a whole library to address it. You should check out Microsoft Velocity before rolling up your own cache. http://msdn.microsoft.com/en-us/data/cc655792.aspx Hope this help.

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

#6


You could use a WeakReference but if your object is not that large than don't because the WeakReference would be taking more memory than the object itself which is not a good technique. Also, if the object is a short-time usage where it will never make it to generation 1 from generation 0 on the GC, there is not much need for the WeakReference but IDisposable interface on the object would have with the release on SuppressFinalize.

您可以使用WeakReference但是如果您的对象不是那么大,因为WeakReference将占用比对象本身更多的内存,这不是一个好技术。此外,如果对象是短时间使用,它将永远不会从GC的第0代进入第1代,则不需要WeakReference,但对象上的IDisposable接口将与SuppressFinalize上的版本一起使用。

If you want to control the lifetime you need a timer to update the datetime/ timespan again the desiredExpirationTime on the object in your cache.

如果要控制生命周期,则需要一个计时器来再次更新日期时间/时间跨度缓存中对象上的desiredExpirationTime。

The important thing is if the object is large then opt for the WeakReference else use the strong reference. Also, you can set the capacity on the Dictionary and create a queue for requesting additional objects in your temp bin serializing the object and loading it when there is room in the Dictionary, then clear it from the temp directory.

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