【发布时间】:2014-10-08 19:23:44
【问题描述】:
我有两个同级类A 和B,我想重构它们,使A 是B 的父类,所以B 可以共享A 的代码。但是这种重构意味着,对于一个关键功能,将互斥锁锁定两次而不是一次。有什么理由不这样做?
A 类
class A{
std::map<std::string, int> values;
std::mutex mutex;
public:
//init and access functions elided
void foo(std::string key, int v){
auto i = values.find(key);
if(i == values.end())return; //Actually report error
{
std::lock_guard<std::mutex> lock(mutex);
i->second = v;
}
}
};
B 类
class B{
std::map<std::string, int> values;
std::map<std::string, std::vector<int> > history;
std::mutex mutex;
public:
//init and access functions elided
void foo(std::string key, int v){
auto i = values.find(key);
if(i == values.end())return; //Actually report error
auto i2 = history.find(key);
if(i2 == history.end())return; //Actually report error
{
std::lock_guard<std::mutex> lock(mutex);
i->second = v;
i2->second.push_back(v);
}
}
};
我想写的 B 类的替代方案是:
class C:public A{
std::map<std::string, std::vector<int> > history;
public:
//init and access functions elided
void foo(std::string key, int v){
A::foo(key,v);
auto i2 = history.find(key);
if(i2 == history.end())return; //Actually report error
{
std::lock_guard<std::mutex> lock(mutex);
i2->second.push_back(v);
}
}
};
访问函数也使用互斥锁来锁定它们的读取。出于这个问题的目的,假设 foo() 比这些类中的任何其他函数都被调用得更多。我们也可以假设所有对foo() 的调用都是序列化的;互斥锁用于其他使用访问函数的线程。
问。将一个互斥锁拆分为两个串行完成的锁不会增加任何新的死锁可能性?
问。与 A 类和 B 类的代码重复,或“隐藏”基类调用中的额外互斥锁是否会产生更大的代码异味?
问。与我在foo() 中执行的其他操作相比,锁定两次的额外开销是否微不足道? IE。我猜插入映射和向量所花费的时间至少是锁定互斥锁的 10 倍。
问。 class C 现在允许读取 values 与读取 history 不同步(即,如果另一个线程在 C::foo() 中间抓住了锁)。如果这被证明是一个问题,那么返回“复制 A 类和 B 类中的代码”是唯一的设计选择吗?
【问题讨论】:
-
IMO,您提出的最大问题是最后一个问题。如果
A::foo和i2->second.push_back(v)之间的数据不一致,那么这是错误的,您将遇到竞争情况。并且根本没有办法使用A::foo并保持单个锁来执行此操作(除非您使用递归互斥锁并锁定整个调用。) -
if之后的一行上多余的左大括号令人困惑……return并不明显(你讨厌空格?)乍一看,它看起来像互斥锁如果条件为真,则采取 -
用户可以直接拨打
A::foo吗?如果是,那么您已经可以读取与history不同步的values,因为它们可以独立更改。 -
像这样的细粒度锁定通常不起作用。这完全取决于您对
history成员所做的else。仅仅在另一个成员函数中返回它的内容已经创建了一个陈旧的视图,它与你通过重构创建的竞赛没有什么不同。 -
@JonathanWakely 大括号是多余的吗?我放了大括号以显示锁的范围,因此在该块之后的任何代码中都不会持有锁。 (我看到我的代码已被编辑以缩进锁定范围;我没有这样做来区分它,现在它看起来确实让我更加困惑;但我不知道您的评论是在编辑之前还是之后!)
标签: c++ multithreading c++11