我想从数据库加载一些对象并缓存它们。这很简单:
public class Dal {
public Entity GetEntity(int id) {
var cacheKey = string.Format(".cache.key.{0}", id);
var item = Cache.Get(cacheKey) as Entity;
if(item == null) {
item = LoadEntityFromDatabase(id);
Cache.Add(cacheKey, item);
}
return item;
}
}
这很简单。但是当多个线程想要访问Dal.GetEntity
时,我想lock
一个与id
有界的对象。我能想到的场景是这样的:
public class Dal {
private static readonly ConcurrentDictionary<int, object>
_locks = new ConcurrentDictionary<int, object>();
public Entity GetEntity(int id) {
var cacheKey = string.Format(".cache.key.{0}", id);
var item = Cache.Get(cacheKey) as Entity;
if(item == null) {
var lockObject = _locks.GetOrAdd(id, new object());
lock (lockObject) {
if(item == null) {
// all exceptions are handled in this block.
item = LoadEntityFromDatabase(id);
Cache.Add(cacheKey, item);
}
}
// * Here is the removing problem:
_locks.TryRemove(id, out lockObject);
}
return item;
}
}
目标是:当一个线程从数据库中获取时,我想锁定id
。实际上我想要防止从数据库请求一个对象两次。我觉得它似乎有效。但我不是一个多线程程序员,也不是lock
人。所以,问题是:我对这种情况采取了哪些风险?
另外,我有这个问题:如何防止字典变得越来越大?如您所见, - 我标有*
- 的地方,我正在尝试使用TryRemove
方法删除已使用的项目。但这似乎是一个愚蠢的举动:如果它试图删除一个对象,而该对象被另一个线程锁定怎么办?我是对的吗?
答案 0 :(得分:0)
我强烈建议你不要混用ConcurrentDictionary和锁 可以使用锁和普通词典,也可以使用ConcurrentDictionary和无锁 你目前正冒着比赛的风险 - 例如当3个线程作用于同一记录时。线程删除旧锁 - B线程使用旧锁,C线程创建新锁,以便B和C可以并行工作。
答案 1 :(得分:0)
这样的事情。 (未经测试);
public class Dal
{
private sealed class Locker
{
private static readonly object DictionaryLocker = new object();
private static readonly Dictionary<int, Locker>
Locks = new Dictionary<int, Locker>();
private readonly object _lockerObject;
private int _num;
private Locker()
{
_num = 0;
_lockerObject = new object();
}
public static object StartLock(int id)
{
Locker locker;
lock (DictionaryLocker)
{
if (!Locks.TryGetValue(id, out locker))
{
locker = new Locker();
Locks.Add(id, locker);
}
++locker._num;
}
return locker._lockerObject;
}
public static void EndLock(int id)
{
lock (DictionaryLocker)
{
Locker locker = Locks[id];
--locker._num;
if (locker._num == 0)
Locks.Remove(id);
}
}
}
public Entity GetEntity(int id)
{
var cacheKey = string.Format(".cache.key.{0}", id);
var item = Cache.Get(cacheKey) as Entity;
if (item != null)
return item;
object lockObject = Locker.StartLock(id);
lock (lockObject)
{
// all exceptions are handled in this block.
item = LoadEntityFromDatabase(id);
Cache.Add(cacheKey, item);
Locker.EndLock(id);
}
return item;
}
}
编辑已更改为Dictionary