【问题标题】:Atomic pointers in c++ and passing objects between threadsC++中的原子指针和线程之间传递对象
【发布时间】:2015-01-03 09:49:06
【问题描述】:

我的问题涉及std::atomic<T*> 和这个指针指向的数据。如果在线程 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(支持原子的版本)。

【问题讨论】:

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


【解决方案1】:

答案是肯定的,也许不是

内存模型原理:

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

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

  • C++ 标准第 29.3/3 节解释了这对 atomics 的工作原理:“所有 memory_order_seq_cst 操作都应有一个总顺序 S,与 一致”发生在” 所有受影响位置的顺序和修改顺序,这样每个 memory_order_seq_cst 加载值的操作会根据此顺序 S 观察最后的先前修改,或者不是 memory_order_seq_cst 的操作的结果。"

  • 第 1.10/5 节解释了这如何影响非原子操作:“库定义了许多被特别标识​​为同步的原子操作 (...)操作。这些操作在使一个线程中的分配对另一个线程可见方面发挥着特殊作用。"

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

非原子数据的风险

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

假设第一个执行场景:

(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) 在读取指针时安全地获取值 &amp;A。内存排序确保在ptr 之前进行的所有分配都可以被其他线程看到(“发生在”约束之前)。

但假设第二个执行场景:

(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 ! 

【讨论】:

    【解决方案2】:

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

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

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

    【讨论】:

    • +1。 (实际上默认是std::memory_order_seq_cst,它比获取/释放有更强的保证,但实际上这就是这里所需要的全部。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-11-23
    • 1970-01-01
    • 1970-01-01
    • 2012-12-17
    • 2019-11-13
    • 2010-10-26
    • 1970-01-01
    相关资源
    最近更新 更多