我有两个同级班,A
和B
,我想重构它们,以便A
是B
的父级,所以{{ 1}}可以共享B
的代码。但是,对于一个关键功能,这个重构意味着锁定互斥锁两次而不是一次。有什么理由没有?
A类
A
B类
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);
}
}
};
访问函数也使用互斥锁来锁定它们的读取。出于这个问题的目的,假设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()
的调用都是序列化的;互斥体用于其他使用访问函数的线程。
Q值。将一个互斥锁拆分为两个串行锁,不能添加任何新的死锁潜力吗?
Q值。代码重复与A类和B类有没有更大的代码气味,或者来自&#34;隐藏&#34;基类调用中的额外互斥锁?
Q值。与我在foo()
中执行的其他操作相比,锁定两次的额外开销是否微不足道?即我猜测插入地图和向量的时间至少是锁定互斥锁的10倍。
Q值。 foo()
现在允许读取与class C
读取不同步的values
(即,如果另一个线程在history
中间抓住锁定)。如果结果证明是一个问题,那就回到&#34;复制A类和B类的代码。唯一的设计选择?
答案 0 :(得分:1)
这个替代方法怎么样,它会添加一个返回锁的foo_impl
函数,因此可以在C::foo
中重复使用:
class A
{
std::map<std::string, int> values;
std::mutex mutex;
public:
//init and access functions elided
void foo(std::string key, int v)
{
foo_impl(key, v);
}
protected:
std::unique_lock<std::mutex> foo_impl(std::string key, int v)
{
auto i = values.find(key);
if (i == values.end()) return {}; //Actually report error
std::unique_lock<std::mutex> lock(mutex);
i->second = v;
return lock;
}
};
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)
{
auto i2 = history.find(key);
if (i2 == history.end()) return; //Actually report error
if (auto lock = A::foo_impl(key,v))
i2->second.push_back(v);
}
};
这可确保A::values
和C::history
的更新在单个锁定下完成,因此A::values
无法在原始C::foo
中的两个锁之间再次更新。{ / p>
答案 1 :(得分:-1)
我不明白为什么你认为有必要锁两次,这看起来像你想做的?
class A{
std::map<std::string, int> values;
std::mutex mutex;
protected:
void foo_unlocked(std::string key, int v){
auto i = values.find(key);
if(i != values.end())
i->second = v;
}
};
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){
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_unlocked(key, v); // do the operation in A but unlocked...
}
};