基于ID字段的并发对象锁

时间:2009-12-09 10:05:46

标签: c# locking

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

也许我可以做这样的事情,但我不喜欢它(创建了太多的对象,而每天只有一两个具有相同ID的消息可以被消耗而且锁定(_lockers)有点耗费时间:< / p>

    private readonly Dictionary<int,object> _lockers = new Dictionary<int,object>();
    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)

6 个答案:

答案 0 :(得分:12)

正如评论中所指出的,一种方法是拥有一个固定的锁池(比如32),并采用ID模32来确定要采取的锁。这会导致一些错误的锁共享。 32是从空中挑选的数字 - 这取决于你的ID值分布,消费者数量等等。

答案 1 :(得分:2)

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

答案 2 :(得分:2)

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

编辑:我没有仔细阅读,这是关于创建的(大)辅助对象的数量 我认为达​​米恩对此有一个好主意,我将把这一点留下来:

关于

  

注意:与ID相同的问题   属性是字符串的类型(在那里   也许我可以锁定   string.Internal(currentObject.ID)

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

答案 3 :(得分:0)

请参阅How to: Synchronize a Producer and a Consumer Thread (C# Programming Guide)

  

除了简单的预防   用锁同时访问   关键字,进一步同步   由两个事件对象提供。一个是   用于向工作线程发出信号   终止,另一个使用   生产者线程发信号给   新项目具有的消费者线程   已被添加到队列中。这两个   事件对象封装在一个   类叫SyncEvents。这允许   要传递给对象的事件   代表消费者和   生产者很容易解决。

- 编辑 -

我写回来的simple code snippet;看看这是否有帮助。我认为这就是weismat指向的目标?

- 编辑 -

如何关注:

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

    • 对象类型
    • 的对象
    • 还有一个布尔 - 例如,bool bInProgress
  2. 将举行

  3. 的词典

    现在检查以下内容

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

如何从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<int, ID> ids = new Dictionary<int, ID>();
    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池。