C++ 新表达式求值顺序(指针赋值)
考虑以下代码:C++ 新表达式求值顺序(指针赋值),c++,multithreading,c++11,C++,Multithreading,C++11,考虑以下代码: Class* p = nullptr; //global var 此代码由线程1执行: p = new Class; 此代码在线程2上执行: if (p != nullptr) ...; // does the standard gurantee that the pointer will be assigned only after object is constructed ? 我的问题是,当p将被分配给指向已分配内存时,标准是否强制执行? 例1: new表达式调用运
Class* p = nullptr; //global var
此代码由线程1执行:
p = new Class;
此代码在线程2上执行:
if (p != nullptr) ...; // does the standard gurantee that the pointer will be assigned only after object is constructed ?
我的问题是,当p
将被分配给指向已分配内存时,标准是否强制执行?
例1:
- new表达式调用运算符new
分配给指向新分配内存的点p
- 调用类的c`tor并将分配的内存传递给它
- new表达式调用运算符new
- 调用类的c`tor并将分配的内存传递给它
分配给指向新分配内存的点p
对于
p=新类的执行顺序
,与示例2中类似,因为首先计算新类
,然后发生赋值(前提是类
或运算符new
的构造函数未引发异常)
[expr.ass]/1:
在所有情况下,赋值顺序都在值之后
计算右操作数和左操作数,然后计算赋值表达式的值
不,标准不保证这样的事情 要解决此问题,您需要在构建对象和分配指针之间设置内存屏障,以便在它们之间的关系发生之前存在线程间:
Class* tmp = new Class();
// you need a memory barrier here
p = tmp;
在c++11中,使用std::atomic
引入内存屏障:
std::atomic<Class*> p;
你为什么不在构造器里睡一觉,看看你自己,分享你学到的知识呢?:)根据,答案是“否”:在对象在所述存储中构造之前,您可能已将
p
指向已分配的存储。没有任何保证(事实上,您的示例调用了未定义的行为)。这正是p必须是原子变量的原因之一。原子变量的默认排序约束也将保证构造函数在被分配给p之前是完整的。赫伯·萨特(Herb Sutter)的原子武器演讲很好地解释了这种行为。读一读关于双重锁定模式的文章,关于它的文章非常详细地解释了为什么这不起作用。@AlexanderBalabin Yep我读过关于锁定模式的文章,我看到的大多数双重检查锁定模式的示例都是这样做的:Class*tmp=newclass代码>和施工后pClass=tmp
;这就是我问这个问题的原因。好吧,如果我们把第二条线放在一边呢。当分配p
时,标准是否强制执行?(在施工结束或开始之前)?这是错误的。在所有情况下,赋值都是在计算右操作数和左操作数-[expr.ass]的值之后排序的/1@Anton这就是C++标准所说的,现在重新排序CPU和从另一个线程观察内存的方式如何?如果sequenced after表示线程之间的可见性顺序正确,那么我们就不需要语言中的原子。此外,如果我的理解正确,编译器仍然可以跨“sequenced before”对指令进行重新排序,前提是它可以保证在当前执行线程中观察到相同的结果。“sequenced before”表示“在之前发生”(参见N4140[intro.multithread]/14)。但是,是的,需要具有acquire-release语义的原子来扩展与另一个线程的“在之前发生”关系(并避免UB的数据争用)。是否更正确地说,参与者和赋值之间需要在之前发生线程间?
p.store(tmp, std::memory_order_release);