新表达式评估顺序(指针赋值)

时间:2015-06-26 07:39:08

标签: 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 expression call operator new
  • p被指定为指向新分配的内存
  • Class调用c`tor并将分配的内存传递给它

示例2:

  • new expression call operator new
  • Class调用c`tor并将分配的内存传递给它
  • p被指定为指向新分配的内存

2 个答案:

答案 0 :(得分:6)

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

C ++ 11草案N3337,

[intro.multithread] / 4:

  

两个表达式评估冲突如果其中一个修改了内存位置(1.7)而另一个修改了内存位置   访问或修改相同的内存位置。

[intro.multithread] / 21:

  

如果程序的执行包含数据竞争,如果它在不同的线程中包含两个冲突的动作,   其中至少有一个不是原子的,也不会发生在另一个之前。任何此类数据竞赛都会产生   未定义的行为。

C ++ 14的相应引用基本相同。

至于p = new Class;的执行顺序,与示例2中的执行顺序类似,因为首先评估new Class,然后进行赋值(提供Class的构造函数或operator new没有抛出异常。)

[expr.ass] / 1:

  

在所有情况下,分配都在值之后排序   计算右和左操作数,并在赋值表达式的值计算之前。

答案 1 :(得分:0)

不,该标准不保证这样的事情。

要修复它,你需要在构造对象和指定指针之间有一个内存屏障,以便它们之间存在线程之间发生的关系:

Class* tmp = new Class();
// you need a memory barrier here
p = tmp;

在c ++ 11中,您使用std::atomic来引入内存障碍:

std::atomic<Class*> p;

在这种情况下,最好使用store()而不是分配:

p.store(tmp, std::memory_order_release);