ipa*_*dop 7 c++ thread-safety c++11
假设我们想A使用一个类使线程安全std::mutex.我的复制构造函数和赋值运算符与下面的代码类似:
#include <mutex>
class A {
private:
int i;
mutable std::mutex mtx;
public:
A() : i(), mtx() { }
A(const A& other) : i(), mtx()
{
std::lock_guard<std::mutex> _lock(other.mtx);
i = other.i;
}
A& operator=(const A& other)
{
if (this!=&other) {
std::lock_guard<std::mutex> _mylock(mtx), _otherlock(other.mtx);
i = other.i;
}
return *this;
}
int get() const
{
std::lock_guard<std::mutex> _mylock(mtx);
return i;
}
};
Run Code Online (Sandbox Code Playgroud)
我不认为它有任何问题,除了在other复制之前被另一个线程销毁的可能性,我可以处理.
我的问题是我没有在任何地方看到这种模式,所以我不知道人们是否只是没有需要,或者由于我目前看不到的原因,这显然是错误的.
谢谢
注意:
这只是一个例子.我可以拥有任意类型的任意数量的成员变量,它不必只是一个int.
在Martin York对可能出现死锁的评论之后,这是一个使用复制和交换的更新版本(复制省略也是可能的,但它无法有效处理自我分配案例).
我也把int改为T,所以人们不能认为它是POD.
template<typename T>
class A {
private:
T t;
mutable std::mutex mtx;
public:
A() : t(), mtx() { }
A(const A& other) : t(), mtx()
{
std::lock_guard<std::mutex> _lock(other.mtx);
t = other.t;
}
A& operator=(const A& other)
{
if (this!=&other) {
A tmp(other);
std::lock_guard<std::mutex> _lock(mtx);
std::swap(t, tmp.t);
}
return *this;
}
T get() const
{
std::lock_guard<std::mutex> _lock(mtx);
return t;
}
};
Run Code Online (Sandbox Code Playgroud)
老问题,新答案:
恕我直言,处理原始复制赋值运算符死锁问题的更好方法是:
A& operator=(const A& other)
{
if (this!=&other) {
std::unique_lock<std::mutex> _mylock(mtx, std::defer_lock),
_otherlock(other.mtx, std::defer_lock);
std::lock(_mylock, _otherlock);
i = other.i;
}
return *this;
}
Run Code Online (Sandbox Code Playgroud)
即用于std::lock(L1, L2)同时锁定两个互斥锁而不用担心死锁。这很可能是比复制/交换成语性能高,特别是如果构件数据由std::vector,std::string或包含载体和/或串的类型。
在 C++1y(我们希望 y 是 4)中,有一个新的<shared_mutex>头文件提供读/写锁定功能,可以提供性能提升(特定用例需要进行性能测试以确认这一点)。以下是它的使用方法:
#include <mutex>
#include <shared_mutex>
class A {
private:
int i;
mutable std::shared_mutex mtx;
public:
A() : i(), mtx() { }
A(const A& other) : i(), mtx()
{
std::shared_lock<std::shared_mutex> _lock(other.mtx);
i = other.i;
}
A& operator=(const A& other)
{
if (this!=&other) {
std::unique_lock<std::shared_mutex> _mylock(mtx, std::defer_lock);
std::shared_lock<std::shared_mutex> _otherlock(other.mtx, std::defer_lock);
std::lock(_mylock, _otherlock);
i = other.i;
}
return *this;
}
int get() const
{
std::shared_lock<std::shared_mutex> _mylock(mtx);
return i;
}
};
Run Code Online (Sandbox Code Playgroud)
即这与原始代码非常相似(修改std::lock为我在上面所做的使用)。但是成员互斥锁类型现在std::shared_mutex不是std::mutex. 并且在保护 a 时const A(并假设除互斥锁之外没有可变成员),只需要将互斥锁锁定在“共享模式”。使用shared_lock<shared_mutex>. 当您需要以“独占模式”锁定互斥锁时,您可以使用unique_lock<shared_mutex>, 或lock_guard<shared_mutex>,并以与使用此功能相同的方式使用std::mutex.
特别要注意的是,现在许多线程可以同时从同一个复制构造A,甚至从同一个复制分配A。但是一次只有一个线程仍然可以复制分配给同一个线程A。