我正在尝试了解如何在c ++中使用带有对象的互斥锁。我有以下(普通的)多线程代码我用作速度测试:
struct Rope{
int n, steps, offset;
//std::mutex mut;
Rope() {}
Rope(int n, int steps, int offset) : n(n), steps(steps), offset(offset) {}
void compute(){
double a[n];
for (int i=0; i<n; i++)
a[i] = i + offset;
for (int step=0; step<steps; step++)
for (int i=0; i<n; i++)
a[i] = sin(a[i]);
}
};
void runTest(){
int numRuns = 30;
int n = 10000;
int steps = 10000;
std::vector<Rope> ropes;
std::vector<std::thread> threads;
for (int i=0; i<numRuns; i++)
ropes.push_back(Rope(n, steps, i));
for (auto& r : ropes)
threads.push_back(std::thread(&Rope::compute, r));
for (std::thread& t : threads)
t.join();
}
代码运行正常,并且在我的4核机器上看到了~4倍的加速。当然,我不会在绳索中存储任何东西,因此不需要互斥锁。如果我现在假设我确实有一些我需要保护的数据,我想将一个互斥锁附加到Rope并且(例如)在compute()循环中调用std :: lock_guard。但是,如果我取消注释互斥锁,我会收到一堆关于“使用已删除函数”的编译器错误,用于赋值和复制运算符。我在安全锁定物体的目标中缺少什么?
答案 0 :(得分:6)
使类线程安全的直接方法是添加互斥锁属性并在访问器方法中锁定互斥锁
class cMyClass {
boost::mutex myMutex;
cSomeClass A;
public:
cSomeClass getA() {
boost::mutex::scoped_lock lock( myMutex );
return A;
}
};
问题在于这使得该类不可复制。这很重要,特别是如果您想将类的对象存储在容器中。
我可以通过使互斥锁成为静态来使事情发挥作用。
class cMyClass {
static boost::mutex myMutex;
cSomeClass A;
public:
cSomeClass getA() {
boost::mutex::scoped_lock lock( myMutex );
return A;
}
};
但是,这意味着当访问任何其他实例时,类的每个实例都会阻塞,因为它们都共享相同的互斥锁。
理论上,包含非静态互斥锁的类可以通过手动编码复制构造函数和赋值运算符来复制,以便省略互斥锁。但是,这样做很困难,也很繁琐,特别是对于在开发过程中经常发生变化的大量属性的类。
如果一个静态互斥锁在一个被阻塞时阻止访问该类的所有实例是不可接受的,那么最好也是最简单的方法是将互斥锁保持在类外。以这种方式暴露类的内部工作方式似乎很不幸,但替代方法更复杂,因此不可靠,并且当访问类的代码级别处理互斥锁时,我经常发现重要的优化。
答案 1 :(得分:0)
您错过了mutex
无法复制的事实。这是什么意思
制作互斥锁的副本?获取和释放互斥锁的地方
在Rope::compute
,因为所有线程都必须访问
相同的互斥锁,你必须在runTest
中定义并传入它
引用或指针。