1

I have an std::map<int, Object*> ObjectMap. Now I need to update the map and update can happen via multiple threads. So, we lock the map for updates. But every update leads to a lengthy computation and hence leads to lock contention.

Let's consider a following scenario.

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
   }
}

Every Customer is also updated with some computation involved. Now this is a trivial Observer Pattern. But with large number of observers and huge calculation in each observer is a killer for this design. The lock contention goes up in my code. I assume this to b a practical problem but people use smarter ways and I am looking for those smarter ways. I hope I am a little clear this time

Thanks Shiv

shiv chawla
  • 591
  • 1
  • 8
  • 20
  • In the code you showed, you're not updating the `map`.. you're updating the object inside the map.. the map itself doesn't need locking for just that.. you can synch the `update()` method instead of locking map.. of course you'll need to lock the map if one thread is reading it and another writing to it at the same time.. – Kashyap Feb 29 '12 at 03:11
  • 2
    with multithreading problems you should state as much of the problem to solve as possible. Some questions: do updates only affect the values of the objects, or can they be inserts/removals from the map? Do the pointers refer to other objects stored in the same map or do they create a linked list? (if it is a list, can objects be added, or removed from the list?) Is there any relationship on the objects that can be used to your advantage (i.e. can more than one object point to the same third object?) – David Rodríguez - dribeas Feb 29 '12 at 03:14
  • @thekashyap, Yes, the object is updated inside the map but before I access the object, map is locked for anymore activities. I thought it was implicit. Sorry for not being clear. But think of update within a lock. – shiv chawla Feb 29 '12 at 03:52
  • @DavidRodríguez-dribeas. There can be updates/removals/insertions. All the operations are possible. Consider this example, `map` and `Order` has a pointer to `Customer` who placed the order. When order is modified, somehow `Customer` has to be updated. So multiple `Order` objects can have pointer to the same `Customer`. – shiv chawla Feb 29 '12 at 03:58
  • 2
    @shivchawla Given that you're doing inserts and removals you really have no other option than locking the entire tree while the update happens. How can you even know that the id you're using in the other thread will remain valid after the current thread has completed. I think David Rodriguez is right, we need to know more about your structure, to actually be able to help. – Michael Anderson Feb 29 '12 at 04:41
  • Consider using specially designed concurrent containers instead of std::map. For example, Microsoft's PPL and Intel's TBB provide unordered concurrent maps. Perhaps ordered concurrent maps exist too, somewhere. – Alexey Kukanov Feb 29 '12 at 07:49
  • @MichaelAnderson, You are right in saying that the map needs to be locked until the end of entire tree. I was thinking of a workaround to that. Let me give an example of what I am planning to do with some real life entities. `class Order //Subject { vector _customers; //subjects void notify() { //update all Customers} }; – shiv chawla Feb 29 '12 at 14:15

1 Answers1

0

Since update is happening on the element of the map, and does not take the map as an argument, I assume the map is unchanging.

I visualise the structure as a chain of Objects for each map id. Now if chain contains distinct entries (and update doesn't access any elements outside its chain, or any global elements) you can get away with adding a lock to the root element of each chain.

However if objects down the chain are potentially shared then you have a more difficult problem. In that case adding a lock to each object should be enough. You can show that if the chains behave correctly (each node has one child, but children can be shared) then locks must be acquired in a consistent order, meaning there is no chance of a deadlock.

If there is other sharing between chains, then the chance of encountering deadlocks is large.

Assuming you have case 2, then your code will look roughly like this

class Object
{
   Object * next;
   Lock l;
   Data d;
   void update(Data d_new)
   {
     l.lock();
     d = d_new;
     next->update(d_new);
     l.unlock();
   }
};
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
  • This is not I am looking for. Firstly, AnotherObject is a different class. – shiv chawla Feb 29 '12 at 04:05
  • Think of the situation as observer pattern. If subject is stored in a map and there comes a update to the subject, then we have to update all possible observers. But in the meantime, the original subject map is locked and no other subject can be updated. This leads to serious lock contention when update to the subject is very frequent. – shiv chawla Feb 29 '12 at 04:15
  • The reuse of Object, was simply to keep my code short. In essence the AnotherObject class looks identical. I can change the example if its not clear what you need to do. – Michael Anderson Feb 29 '12 at 04:36