c ++中的原子指针和在线程之间传递对象

时间:2014-11-06 18:45:41

标签: c++ multithreading c++11 atomic memory-model

我的问题涉及std :: atomic以及此指针指向的数据。如果在主题1中我有

Object A;
std:atomic<Object*> ptr;
int bar = 2;
A.foo = 4;  //foo is an int;
ptr.store(*A);

如果在线程2中我发现ptr指向A,我可以保证ptr-&gt; foo是4而bar是2吗?原子指针的默认内存模型(顺序一致)是否保证在原子存储之前发生的非原子(在这种情况下为A.foo)上的分配将在其看到同一atomic.store的赋值之前被其他线程看到对于这两种情况?

如果它有帮助或重要,我使用x64(我只关心这个平台),gcc(使用支持原子的版本)。

2 个答案:

答案 0 :(得分:13)

答案是肯定的,也许没有

内存模型原则:

C ++ 11 atomics使用by default std::memory_order_seq_cst内存排序,这意味着操作顺序一致

这样的语义是所有操作的排序就好像所有这些操作都按顺序执行:

  • C ++标准第29.3 / 3节解释了它如何适用于 atomics :&#34; 在所有memory_order_seq_cst操作中应该有一个总订单S,与< strong>“之前发生”所有受影响位置的订单和修改订单,例如每个memory_order_seq_cst 加载值的操作遵循根据此顺序S的最后一个修改,或者不是memory_order_seq_cst的操作的结果。&#34;

  • 1.10 / 5节解释了这种影响也是非原子的:&#34; 该库定义了许多特殊的原子操作(...)被识别为同步操作。这些操作在使一个线程中的分配对另一个线程可见时起着特殊的作用。&#34;

你的问题的答案是肯定的!

非原子数据的风险

但是,您应该意识到,实际上,对于非原子值,一致性保证更加有限。

假设第一个执行方案:

(thread 1) A.foo = 10; 
(thread 1) A.foo = 4;     //stores an int
(thread 1) ptr.store(&A); //ptr is set AND synchronisation 
(thread 2) int i = *ptr;  //ptr value is safely accessed (still &A) AND synchronisation

这里,i是4.因为ptr是原子的,所以线程(2)在读取指针时安全地获得值&A。内存排序确保其他线程可以看到在ptr之前进行的所有分配(&#34;在&#34;约束之前发生)。

但假设第二个执行方案:

(thread 1) A.foo = 4;     //stores an int
(thread 1) ptr.store(&A); //ptr is set AND synchronisation 
(thread 1) A.foo = 8;     // stores int but NO SYNCHRONISATION !! 
(thread 2) int i = *ptr;  //ptr value is safely accessed (still &A) AND synchronisation

此处结果未定义。它可能是4,因为内存排序保证在其他线程看到ptr分配之前发生的事情。但是没有什么可以阻止后来的任务被看到。所以它可能是8.

如果你有*ptr = 8;而不是A.foo=8;那么你会再次确定:i将是8.

您可以通过此实验验证,例如:

void f1() {  // to be launched in a thread
    secret = 50; 
    ptr = &secret; 
    secret = 777; 
    this_thread::yield();
}
void f2() { // to be launched in a second thread
    this_thread::sleep_for(chrono::seconds(2));
    int i = *ptr; 
    cout << "Value is " << i << endl;
}

<强>结论

总而言之,您的问题的答案是肯定的,但前提是在同步后没有对非原子数据进行其他更改。主要风险是只有ptr是原子的。但这不适用于指向的值。

需要注意的是,当您将原子指针重新分配给非原子指针时,特别是指针会带来进一步的同步风险。

示例:

// Thread (1): 
std:atomic<Object*> ptr;
A.foo = 4;  //foo is an int;
ptr.store(*A);

// Thread (2): 
Object *x; 
x=ptr;      // ptr is atomic but x not !  
terrible_function(ptr);   // ptr is atomic, but the pointer argument for the function is not ! 

答案 1 :(得分:5)

默认情况下,C ++ - 11个原子操作具有获取/释放语义。

因此,看到您商店的线程也会看到之前执行的所有操作。

您可以找到更多详细信息here