我有一个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
}
}
每个客户也会进行一些计算更新。
现在这是一个琐碎的观察者模式。但是拥有大量观察者并在每个观察者中进行大量计算是这种设计的杀手er。锁争用在我的代码中上升。我认为这是一个实际问题,但是人们使用更智能的方式,而我正在寻找那些更智能的方式。我希望这次我有点清楚
谢谢
希夫
最佳答案
由于更新发生在地图的元素上,并且没有将地图作为参数,因此我认为地图是不变的。
我将结构可视化为每个地图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();
}
};