这可能有点难以实现,但是否存在一个“C#生产质量的线程安全内存LRU缓存并带有过期时间”? 或者是否有任何最佳实践想法来实现相同的功能?
(LRU是“最近最少使用”- http://en.wikipedia.org/wiki/Cache_algorithms#LRU)
澄清一下:我想在ASP.Net MVC网站中支持一个具有以下接口的内存缓存:
(LRU是“最近最少使用”- http://en.wikipedia.org/wiki/Cache_algorithms#LRU)
澄清一下:我想在ASP.Net MVC网站中支持一个具有以下接口的内存缓存:
public interface ICache
{
T GetOrAdd<T>(string key, Func<T> create, TimeSpan timeToLive) where T : class;
bool Remove(string key);
}
- 我想要使用"GetOrAdd",因为我希望这些操作是"原子性"的——即避免两个线程同时查询缓存时出现竞争条件
- 我想要使用Create函数,因为创建这些对象很昂贵(需要复杂的数据库访问)
- 我需要过期时间,因为这些对象在一段时间后会过期
微软提供的最佳解决方案似乎是"System.Runtime.Caching.MemoryCache",但它似乎有一些注意事项:
- 它需要定期轮询缓存以遵守所强加的内存限制。我不能让我的系统有任何可能耗尽内存的情况。我已经阅读了这篇文章,这让我感到担忧:MemoryCache does not obey memory limits in configuration
- 它似乎只有"AddOrGetExisting"支持我的接口,它需要将构造的对象作为第二个参数——如果这个对象很难创建,那么预先构造它是否不会削弱缓存的优点呢?
代码大致如下:
public sealed class Cache : ICache
{
private readonly MemoryCache _cache;
public Cache()
{
_cache = MemoryCache.Default;
}
public T GetOrAdd<T>(string key, Func<T> create, TimeSpan timeToLive) where T : class
{
// This call kinda defeats the point of the cache ?!?
var newValue = create();
return _cache.AddOrGetExisting(key, newValue, DateTimeOffset.UtcNow + timeToLive) as T;
}
public bool Remove(string key)
{
_cache.Remove(key);
return true;
}
}
或者可能有更好的解决方案,比如使用Lazy < T >,它允许结果仅被创建一次,但感觉像是一个hack(缓存Func是否有后果?):
class Program
{
static void Main(string[] args)
{
Func<Foo> creation = () =>
{
// Some expensive thing
return new Foo();
};
Cache cache = new Cache();
// Result 1 and 2 are correctly the same instance. Result 3 is correctly a new instance...
var result1 = cache.GetOrAdd("myKey", creation, TimeSpan.FromMinutes(30));
var result2 = cache.GetOrAdd("myKey", creation, TimeSpan.FromMinutes(30));
var result3 = cache.GetOrAdd("myKey3", creation, TimeSpan.FromMinutes(30));
return;
}
}
public sealed class Foo
{
private static int Counter = 0;
private int Index = 0;
public Foo()
{
Index = ++Counter;
}
}
public sealed class Cache
{
private readonly MemoryCache _cache;
public Cache()
{
_cache = MemoryCache.Default;
}
public T GetOrAdd<T>(string key, Func<T> create, TimeSpan timeToLive) where T : class
{
var newValue = new Lazy<T>(create, LazyThreadSafetyMode.PublicationOnly);
var value = (Lazy<T>)_cache.AddOrGetExisting(key, newValue, DateTimeOffset.UtcNow + timeToLive);
return (value ?? newValue).Value;
}
public bool Remove(string key)
{
_cache.Remove(key);
return true;
}
}
其他想法:
- 我也找到了这个实现,但它不允许您指定基于时间的过期:是否有IDictionary的任何LRU实现?
- 也许有一个使用ReaderWriterLock的实现?
- 一些包装ConcurrentDictionary的东西?
GetOrAdd
方法可以使用MemoryCache
上可用的Get
和Set
方法来实现,而不必使用AddOrGetExisting
。至于尊重内存限制,在 .NET 中严格意义上实现这一点几乎是不可能的,但除非您的系统未配置虚拟内存(这是一个糟糕的想法,我怀疑这种情况会发生),在实践中,一旦达到高内存使用率,缓存将非常快地驱逐项目,并且不应该导致内存不足错误。 - Mike Marynowski