对象缓存通用锁定
本文关键字:锁定 缓存 对象 | 更新日期: 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>
。任何锁定都可以安全地应用于对特定通用缓存的所有访问,而不是您现在拥有的"聚合"缓存。