Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/163.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 新表达式求值顺序(指针赋值)_C++_Multithreading_C++11 - Fatal编程技术网

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并将分配的内存传递给它
例2:

  • new表达式调用运算符new
  • 调用类的c`tor并将分配的内存传递给它
  • p
    分配给指向新分配内存的点

根据标准,如果两个线程中的这些操作不同步,则行为未定义

C++11草案N3337

[简介.多线程]/4:

如果其中一个修改内存位置(1.7)而另一个修改,则两个表达式求值将发生冲突 访问或修改同一内存位置

[简介.多线程]/21:

如果程序在不同线程中包含两个冲突的操作,则程序的执行包含数据竞争, 其中至少有一个不是原子的,而且两个都不是在另一个之前发生的。任何这样的数据竞争都会导致 未定义的行为

C++14中相应的引用基本上是相同的


对于
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);