最小化锁争用 c++ std::map

Posted

技术标签:

【中文标题】最小化锁争用 c++ std::map【英文标题】:Minimize lock contention c++ std::map 【发布时间】:2012-02-29 03:04:51 【问题描述】:

我有一个std::map<int, Object*> ObjectMap。现在我需要更新地图,更新可以通过多个线程进行。因此,我们锁定地图以进行更新。但是每次更新都会导致冗长的计算,从而导致锁争用。

让我们考虑以下场景。

class Order  //Subject      
 double _a, _b,_c;   
  std::vector<Customer* > _customers;  

  public:           
  void notify(int a, int b. int c)
      
     //update all customers via for loop. assume a for loop and iterator i    
     _customers[i] ->updateCustomer(a,b,c)
        

;
class SomeNetworkClass

private:
   std::map<int, Order*> _orders;
public:
   void updateOrder(int orderId, int a, int b, intc)
        
    //lock the map
      Order* order = _orders[orderId];
      order->notify();
    //release the lock
        


class Customer

public:
   void updateCustomer(int a,int b, int c)
   
    //some lengthy function. just for this example. 
    //assume printing a, b and c multiple times
   

每个客户也会通过一些计算进行更新。 现在这是一个微不足道的观察者模式。但是,大量的观察者和每个观察者的巨大计算是这种设计的杀手锏。锁定争用在我的代码中上升。我认为这是一个实际问题,但人们使用更聪明的方法,我正在寻找那些更聪明的方法。希望这次能说清楚一点

谢谢 湿婆

【问题讨论】:

在您显示的代码中,您没有更新 map.. 您正在更新地图内的对象.. 地图本身不需要锁定.. 您可以同步update() 方法而不是锁定映射。当然,如果一个线程正在读取映射,另一个线程同时写入映射,则需要锁定映射。.. 对于多线程问题,您应该尽可能多地说明要解决的问题。一些问题:更新是否只影响对象的值,还是可以从地图中插入/删除?指针是指存储在同一个映射中的其他对象还是创建一个链表? (如果它是一个列表,可以从列表中添加或删除对象吗?)对象上是否有任何关系可以利用您的优势(即多个对象可以指向同一个第三个对象?)跨度> @thekashyap,是的,该对象已在地图内更新,但在我访问该对象之前,地图已被锁定以进行更多活动。我以为是隐含的。抱歉不清楚。但是想想锁内的更新。 @DavidRodríguez-dribeas。可以有更新/删除/插入。所有的操作都是可能的。考虑这个例子,map&lt;id, Order*&gt;Order 有一个指向下订单的Customer 的指针。修改订单时,必须以某种方式更新Customer。所以多个Order 对象可以有指向同一个Customer 的指针。 @shivchawla 鉴于您正在执行插入和删除操作,除了在更新发生时锁定整个树之外,您真的别无选择。你怎么知道你在另一个线程中使用的 id 在当前线程完成后仍然有效。我认为 David Rodriguez 是对的,我们需要更多地了解您的结构,才能真正提供帮助。 【参考方案1】:

由于更新发生在地图的元素上,并且不将地图作为参数,我假设地图是不变的。

我将结构可视化为每个地图 ID 的对象链。现在,如果链包含不同的条目(并且 update 不会访问其链之外的任何元素或任何全局元素),您可以为每个链的根元素添加锁。

但是,如果链中的对象可能被共享,那么您将遇到更困难的问题。在这种情况下,为每个对象添加一个锁就足够了。您可以证明,如果链的行为正确(每个节点有一个子节点,但子节点可以共享),则必须以一致的顺序获取锁,这意味着不会出现死锁。

如果链之间有其他共享,那么遇到死锁的机会就很大。

假设您有案例 2,那么您的代码将大致如下所示

class Object

   Object * next;
   Lock l;
   Data d;
   void update(Data d_new)
   
     l.lock();
     d = d_new;
     next->update(d_new);
     l.unlock();
   
;

【讨论】:

这不是我要找的。首先,AnotherObject 是一个不同的类。 把这种情况想象成观察者模式。如果主题存储在地图中并且主题有更新,那么我们必须更新所有可能的观察者。但与此同时,原来的主题图被锁定,不能更新其他主题。当对主题的更新非常频繁时,这会导致严重的锁争用。 对象的重用,只是为了保持我的代码简短。本质上,AnotherObject 类看起来是相同的。如果不清楚您需要做什么,我可以更改示例。

以上是关于最小化锁争用 c++ std::map的主要内容,如果未能解决你的问题,请参考以下文章

检测闩锁/自旋锁争用

InnoDB快速定位行锁争用会话的过程和操作

解释 AWR 报告中的行锁争用

Linux -- 管理锁争用(翻译)

多线程中的 std::map 奇怪的资源争用

使用 lambda 函数在 std::unordered_map 中查找最小值