c#生产质量的线程安全内存LRU缓存

本文关键字:安全 内存 LRU 缓存 线程 | 更新日期: 2023-09-27 17:50:58

这可能就像要求月亮在一根棍子上;但是是否有一个"c#生产质量的线程安全内存LRU缓存到期?"或者有人有最佳实践的想法来实现同样的事情吗?

(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不服从配置中的内存限制
  • 它似乎只有"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 ,它允许只创建一次结果,但感觉像是一个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;
    }
}

其他想法:

  • 我也发现了这个实现,但它不允许你指定一个基于时间的到期:是否有任何LRU实现字典?
  • 也许有一个实现在那里使用ReaderWriterLock?
  • 关于ConcurrentDictionary的一些包装?

c#生产质量的线程安全内存LRU缓存

我实现了一个为并发工作负载设计的线程安全伪LRU——目前在生产系统中使用。性能非常接近ConcurrentDictionary,比MemoryCache快10倍,命中率比传统的LRU好。下面的github链接提供了完整的分析。

用法如下:

int capacity = 666;
var lru = new ConcurrentTLru<int, SomeItem>(capacity, TimeSpan.FromMinutes(5));
var value = lru.GetOrAdd(1, (k) => new SomeItem(k));
bool removed = lru.TryRemove(1);

GitHub: https://github.com/bitfaster/BitFaster.Caching

Install-Package BitFaster.Caching