对于以互斥为成员的类,当我创建复制构造函数时,我需要决定要锁定哪个互斥锁。对于以下代码,我想知道为什么我只需要锁定 rhs.mu _ 但不必锁定 this-> mu _ ?是否有可能多个线程为同一个对象调用复制构造函数?
class Obj {
public:
std::mutex mu_;
std::string data_;
// copy ctor
Obj(const Obj& rhs) {
std::unique_lock<std::mutex> lk(rhs.mu_); // why only lock rhs.mu_?
data_ = rhs.data_;
}
}
更新 这段代码是否使用 new ?
同时调用copy ctorObj* t = nullptr;
Obj someObj;
// ... populate someObj
std::thread t1([&]() { t = new Obj(someObj); });
std::thread t2([&]() { t = new Obj(someObj); });
答案 0 :(得分:3)
如果你的变量是本地的,那么在构造过程中它是其他线程无法访问的,因为其他线程不能在这个线程中命名一个局部变量。
如果您的变量具有静态生命周期,则C ++标准保证线程安全:
第一次控件通过其声明时,执行具有静态存储持续时间或线程存储持续时间的块范围变量的动态初始化;
...
如果控件在初始化变量时同时进入声明,则并发执行应等待初始化完成。
因此构造函数不会被调用两次。
答案 1 :(得分:2)
您需要锁定复制对象的互斥锁(以确保您以一致的状态复制对象),而您可以单独保留正在构造的对象的锁。
对于构造函数来说,这是正确的:根据定义,构造函数调用是单线程的:
static
本地,static
类字段)保证按标准初始化一次(编译器注入类似于call_once
的东西); new
分配的对象再次是安全的,因为运行像new A
这样的表达式的每个线程将分配一个不同的实例,构造函数将在该实例上运行。你可以遇到的唯一问题是,如果你的某个客户使用展示位置new
,但在这种情况下,我认为调用者有责任不同时调用展示位置new
构造函数。相同的对象 - 如:
new
两次(之间没有析构函数调用)是调用者的合同违规,因此在构造函数中通过互斥锁序列化不会解决任何问题; 所以,长话短说,不要担心在构造中锁定正在构造的对象。该语言保证对象构造是非并发的。