Свести к минимуму конкуренцию за блокировку C++ std::map

У меня есть 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
   }
}

Каждый клиент также обновляется с некоторыми задействованными вычислениями. Теперь это тривиальный шаблон Observer. Но с большим количеством наблюдателей и огромным расчетом в каждом наблюдателе это убийственно для этой конструкции. Конфликт за блокировку возникает в моем коде. Я предполагаю, что это практическая проблема, но люди используют более разумные способы, и я ищу эти более разумные способы. Надеюсь, на этот раз я немного ясно выразился

Спасибо Шив


person shiv chawla    schedule 29.02.2012    source источник
comment
В коде, который вы показали, вы не обновляете map.. вы обновляете объект внутри карты.. сама карта не нуждается в блокировке только для этого.. вы можете синхронизировать метод update() вместо блокировки карты. , конечно, вам нужно будет заблокировать карту, если один поток читает ее, а другой записывает в нее одновременно.   -  person Kashyap    schedule 29.02.2012
comment
с проблемами многопоточности вы должны указать как можно больше проблем, которые нужно решить. Несколько вопросов: обновления влияют только на значения объектов или это могут быть вставки/удаления с карты? Ссылаются ли указатели на другие объекты, хранящиеся на той же карте, или они создают связанный список? (если это список, могут ли объекты быть добавлены или удалены из списка?) Существуют ли какие-либо отношения между объектами, которые можно использовать в ваших интересах (т. е. могут ли более одного объекта указывать на один и тот же третий объект?)   -  person David Rodríguez - dribeas    schedule 29.02.2012
comment
@thekashyap, да, объект обновляется внутри карты, но до того, как я получу доступ к объекту, карта блокируется для дальнейших действий. Я думал, что это скрыто. Извините за неясность. Но подумайте об обновлении в замке.   -  person shiv chawla    schedule 29.02.2012
comment
@DavidRodríguez-dribeas. Могут быть обновления/удаления/вставки. Возможны все операции. Рассмотрим этот пример, map<id, Order*> и Order имеют указатель на Customer, разместившего заказ. Когда порядок изменен, каким-то образом Customer необходимо обновить. Таким образом, несколько объектов Order могут иметь указатель на один и тот же Customer.   -  person shiv chawla    schedule 29.02.2012
comment
@shivchawla Учитывая, что вы выполняете вставки и удаления, у вас действительно нет другого выбора, кроме как заблокировать все дерево во время обновления. Как вы можете знать, что идентификатор, который вы используете в другом потоке, останется действительным после завершения текущего потока. Я думаю, что Давид Родригес прав, нам нужно больше узнать о вашей структуре, чтобы действительно иметь возможность помочь.   -  person Michael Anderson    schedule 29.02.2012
comment
Рассмотрите возможность использования специально разработанных параллельных контейнеров вместо std::map. Например, PPL от Microsoft и TBB от Intel предоставляют неупорядоченные параллельные карты. Возможно, где-то тоже существуют упорядоченные параллельные карты.   -  person Alexey Kukanov    schedule 29.02.2012
comment
@MichaelAnderson, вы правы, говоря, что карту нужно заблокировать до конца всего дерева. Я думал об обходном пути к этому. Позвольте мне привести пример того, что я планирую сделать с некоторыми реальными сущностями. `class Order //Subject { vector‹Customer› _customers; //субъекты void notify() { //обновить всех клиентов} };   -  person shiv chawla    schedule 29.02.2012


Ответы (1)


Поскольку обновление происходит в элементе карты и не принимает карту в качестве аргумента, я предполагаю, что карта неизменна.

Я визуализирую структуру как цепочку объектов для каждого идентификатора карты. Теперь, если цепочка содержит отдельные записи (и обновление не обращается ни к каким элементам за пределами этой цепочки или к каким-либо глобальным элементам), вы можете обойтись без добавления блокировки к корневому элементу каждой цепочки.

Однако, если объекты в цепочке потенциально являются общими, у вас есть более сложная проблема. В этом случае достаточно добавить блокировку к каждому объекту. Вы можете показать, что если цепочки ведут себя правильно (у каждого узла есть один дочерний узел, но дочерние узлы могут быть общими), то блокировки должны устанавливаться в согласованном порядке, что означает отсутствие возможности взаимоблокировки.

Если есть другой обмен между цепочками, то вероятность столкнуться с взаимоблокировками велика.

Предполагая, что у вас есть случай 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();
   }
};
person Michael Anderson    schedule 29.02.2012
comment
Это не я ищу. Во-первых, AnotherObject — это другой класс. - person shiv chawla; 29.02.2012
comment
Думайте о ситуации как о паттерне наблюдателя. Если субъект хранится в карте и происходит обновление субъекта, то мы должны обновить всех возможных наблюдателей. Но в то же время исходная тематическая карта заблокирована, и никакая другая тема не может быть обновлена. Это приводит к серьезной конкуренции за блокировку, когда обновление субъекта происходит очень часто. - person shiv chawla; 29.02.2012
comment
Повторное использование Object было просто для того, чтобы мой код был коротким. По сути, класс AnotherObject выглядит идентично. Я могу изменить пример, если не ясно, что вам нужно сделать. - person Michael Anderson; 29.02.2012