基于ID字段的并发对象锁

我有一个生产者/消费者流程。 消费对象具有ID属性(类型为整数),我想一次只消耗一个具有相同ID的对象。 我怎么能这样做?

也许我可以做这样的事情,但我不喜欢它(创造了太多的对象,而每天只能使用一个或两个具有相同ID的对象并且锁定(_lockers)有点耗费时间:

private readonly Dictionary _lockers = new Dictionary(); private object GetLocker(int id) { lock(_lockers) { if(!_lockers.ContainsKey(id)) _lockers.Add(id,new object()); return _lockers[id]; } } private void Consume(T notif) { lock(GetLocker(notif.ID)) { ... } } enter code here 

注意:同样的问题,ID属性是string类型(在cas中,我可以锁定string.Internal(currentObject.ID)

如注释中所示,一种方法是使用固定的锁池(比如32),并使用ID模32来确定要采用的锁。 这会导致一些错误的锁共享。 32是从空中挑选的数字 – 这将取决于您的ID值分布,消费者数量等。

你能让你的ID对每个对象都是唯一的吗? 如果是这样,您可以只对对象本身应用锁定。

首先,
你有没有lock(_lockers)确定lock(_lockers)确实是一个瓶颈? 因为如果没有破坏,请不要修理它。

编辑:我没有仔细阅读,这是关于创建的(大)辅助对象的数量。
我认为Damien对此有一个好主意,我会对这些字符串留下一些看法:

关于

注意:同样的问题,ID属性是string类型(在cas中,我可以锁定string.Internal(currentObject.ID)

不,不好主意。 你可以锁定一个字符串,但是你必须担心他们可能被拘禁。 很难确定它们是独一无二的。

我会将同步的FIFO队列视为所有生成对象的单独类/单例 – 生成器将对象排队并使消费者出列 – 因此实际对象不再需要任何同步。 然后在实际对象之外完成同步。

如何从ID对象池中分配ID并锁定这些ID?

创建项目时:

 var item = CreateItem(); ID id = IDPool.Instance.Get(id); //assign id to object item.ID = id; 

ID池创建并维护共享ID实例:

 class IDPool { private Dictionary ids = new Dictionary(); public ID Get(int id) { //get ID from the shared pool or create new instance in the pool. //always returns same ID instance for given integer } } 

然后锁定ID,它现在是您的Consume方法中的引用:

 private void Consume(T notif) { lock(notif.ID) { ... } } 

这不是最佳解决方案,只能将问题解决到不同的地方 – 但如果您认为您对锁具有压力,则可以使用此方法获得性能提升(假设您的对象是在单个线程上创建的,那么您然后不需要同步ID池)。

请参见如何:同步生产者和使用者线程(C#编程指南)

除了简单地阻止使用lock关键字进行同时访问之外,还有两个事件对象提供进一步的同步。 一个用于通知工作线程终止,另一个用于生成器线程在新项目添加到队列时向消费者线程发送信号。 这两个事件对象封装在一个名为SyncEvents的类中。 这允许将事件传递给代表消费者和生产者线程的对象。

– 编辑 –

我写的一段简单的代码片段 ; 看看这是否有帮助。 我认为这就是weismat所指向的?

– 编辑 –

怎么样:

  1. 创建一个对象,比如说CCustomer会持有:

    • 对象类型的对象
    • 和布尔 – 例如,布尔bInProgress
  2. 将持有的字典

现在当你检查以下

 if(!_lockers.ContainsKey(id)) _lockers.Add(id,new CCustomer(/**bInProgress=true**/)); return _lockers[id]; **//here you can check the bInProgress value and respond accordingly**.