对象缓存通用锁定

时间:2015-06-15 08:05:59

标签: c# caching

我有一个内存缓存的对象缓存,表示缓存可以容纳多种类型,并且我想在访问{T}时在所述{T}上添加一个锁。

我的实施:

    readonly static IDictionary<Type, List<object>> _cache = new ConcurrentDictionary<Type, List<object>>();

    private static List<object> FindTypeInCache(Type type)
    {
        List<object> list;
        if (_cache.TryGetValue(type, out list))
        {
            return list;
        }
        else
        {
            _cache[type] = new List<object>();
        }

        return new List<object>();
    }

    public static T FindFirstBy<T>(Func<T, bool> predicate) where T : class
    {
        // Is this a valid lock locking only _cache[T] ? And not _cache as whole?
        lock (_cache[typeof(T)])
        {
            return FindTypeInCache(typeof(T)).Cast<T>().Where(predicate).FirstOrDefault();
        }
    }

    public static bool AddOrUpdate<T>(Func<T, bool> predicate, T entity) where T : class
    {
        lock (_cache[typeof(T)])
        {
            // Find Type cache.
            List<object> list = FindTypeInCache(typeof(T));

            // Look for old entity.
            var e = list.Cast<T>().Where(predicate).FirstOrDefault();

            // If no old record exists no problem we treat this as if its a new record.
            if (e != null)
            {
                // Old record found removing it.
                list.Remove(e);
            }

            // Regardless if object existed or not we add it to our Cache.
            list.Add(entity);
            _cache[typeof(T)] = list;
        }
    }

我的实现是否正确在访问时锁定_cache [T]而不是整个_cache?

1 个答案:

答案 0 :(得分:4)

你的代码有很多奇怪的(或完全错误的)。

首先,您使用的是ConcurrentDictionary,但您并非将其用作并发词典。例如,要初始化列表,您需要使用GetOrAddMethod

private static List<object> FindTypeInCache(Type type)
{
    return _cache.GetOrAdd(type, () => new List<object>());
}

简单且线程安全:)

其次,你在_cache[type]上锁定了 - 但即使缓存中没有这样的类型。这意味着KeyNotFoundException

第三,您使用锁定保护的唯一代码是阅读。但这很可能是不够的 - 至少,你需要用相同的锁来保护写作(特别是上面提到的特别棘手),并且取决于你对返回值的实际用法,返回值& #39; s突变(如果它确实是可变的,也可以读取任何可变值)。

换句话说,您只是设法保护实际上不需要保护的代码(如果您使用正确的方法来更新字典)! lock周围的额外Where可能有助于轻微,但它确实无法使List访问安全。

所有这一切,无论如何,也许还有更好的解决方案。您正在使用通用方法来使用缓存。为什么不让缓存本身通用?这样,您首先会避免使用字典,因为您在字典中存储的每个泛型类型都将获得自己的类型 - 这也意味着您可以初始化List<T>在一个静态构造函数安全。任何锁定都可以安全地应用于对特定通用缓存的所有访问,而不是&#34;聚合&#34;你现在拥有的缓存。