对象缓存通用锁定

本文关键字:锁定 缓存 对象 | 更新日期: 2023-09-27 17:56:07

我有一个内存缓存的对象缓存,所述缓存可以容纳多种类型,每当访问给定的{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?

对象缓存通用锁定

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

首先,您使用的是ConcurrentDictionary,但未将其用作并发字典。例如,要初始化列表,您可以使用GetOrAddMethod

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

简单且线程安全的:)

其次,您锁定了_cache[type] - 但即使缓存中没有此类类型。这意味着KeyNotFoundException.

第三,你用锁保护的唯一代码是读数。但这很可能还不够 - 至少,您还需要使用相同的锁保护写入(考虑到上述观点,这尤其棘手),并且根据您对返回值的实际使用情况,返回值的突变(如果它确实是可变的,该可变值的任何读取也是如此)。

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

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