C#生产质量线程安全的内存中LRU缓存有效期吗?

时间:2015-05-27 17:58:45

标签: c# asp.net-mvc multithreading collections lru

这可能就像在棍子上要求月亮一样;但有没有一个" 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);
}
  • 我想&#34; GetOrAdd&#34;因为我希望这些操作是&#34; atomic&#34; - 即避免试图同时查询缓存的两个线程周围的竞争条件
  • 我想要Create函数,因为这些对象的创建很昂贵(需要复杂的数据库访问)
  • 我需要到期,因为这些对象会在一段时间后过期

Microsoft的最佳解决方案似乎是&#34; System.Runtime.Caching.MemoryCache&#34;但是它似乎有几个警告:

  • 需要定期轮询缓存以遵守强加的内存限制。我的系统中没有任何内存不足的可能性。我看过这篇文章,让我担心:MemoryCache does not obey memory limits in configuration
  • 它似乎只有&#34; AddOrGetExisting&#34;支持我的接口,它将构造的对象作为第二个参数 - 如果这个对象创建起来很昂贵,不预先构建它有点击败缓存点吗?

代码看起来像:

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&gt;,它确实只允许创建一次结果,但感觉就像是一个黑客(缓存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;
    }
}

其他想法:

2 个答案:

答案 0 :(得分:5)

我实现了一个为并行工作负载设计的线程安全伪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

答案 1 :(得分:-1)

我添加了LRU缓存的实现

https://github.com/mohsenShakiba/LRUCache