我有一个内存缓存的对象缓存,表示缓存可以容纳多种类型,并且我想在访问{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?
答案 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;你现在拥有的缓存。