我有一个方法需要独占地运行一段代码,但是我只想在真正需要的时候添加这个限制。根据Id值(Int 32),我将加载/修改不同的对象,所以锁定所有线程的访问是没有意义的。下面是第一次尝试这样做-
private static readonly ConcurrentDictionary<int, Object> LockObjects = new ConcurrentDictionary<int, Object>();
void Method(int Id)
{
lock(LockObjects.GetOrAdd(Id,new Object())
{
//Do the long running task here - db fetches, changes etc
Object Ref;
LockObjects.TryRemove(Id,out Ref);
}
}
我怀疑这是否可行-TryRemove可能会失败(这将导致ConcurrentDictionary不断变大)。
一个更明显的错误是,TryRemove成功地删除了对象,但如果有其他线程(相同ID)正在等待(锁定)此对象,然后一个具有相同ID的新线程进入并添加一个新对象并开始处理,因为没有其他线程在等待它刚刚添加的对象。
我应该使用TPL或者某种ConcurrentQueue来排队我的任务吗?最简单的解决方案是什么?
5条答案
按热度按时间e37o9pze1#
我使用类似的方法来锁定相关项的资源,而不是一揽子资源锁定...它工作得很完美。
差不多了,但是你真的不需要从字典中删除这个对象;只让具有该ID的下一个对象获得该对象上的锁。
你的应用程序中唯一标识的数量肯定是有限制的吧?这个限制是什么?
blpfk2vs2#
我看到的主要语义问题是,一个对象可以在没有被列在集合中的情况下被锁定,因为锁中的最后一行删除了它,等待线程可以拾取它并锁定它。
将集合更改为应保护锁的对象集合。不要不要将其命名为
LockedObjects
,并且不要从集合中移除对象,除非您认为不再需要该对象。我总是把这种类型的对象看作是一把钥匙,而不是一把锁或一个被阻塞的对象;对象未被锁定,它是锁定代码序列的密钥。
n53p2ov03#
我使用了下面的方法。不检查原始ID,而是获取int类型的小散列代码来获取现有对象以进行锁。锁的计数取决于您的情况-锁计数器越多,冲突的概率越小。
用法:
当然,您可以使用任何散列代码函数来获取对应的数组索引。
wswtfjt74#
如果你想使用ID本身,并且不允许由哈希代码引起的冲突,你可以使用下一种方法:维护对象的字典,并存储关于想要使用ID的线程数量的信息:
用法:
kgsdhlau5#
有一些迷你库可以帮你做到这一点,比如AsyncKeyedLock,我已经用过了,它省去了我很多麻烦。