C++ 中的原子指针以及在线程之间传递对象

2023-12-22

我的问题涉及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->foo是 4 并且bar is 2?

原子指针的默认内存模型(顺序一致)是否保证非原子指针的赋值(在本例中)A.foo)在原子存储被其他线程看到相同的分配之前发生atomic.store对于这两种情况?

如果有帮助或重要的话,我正在使用 x64(而且我只关心这个平台)、gcc(具有支持原子的版本)。


答案是肯定的,也许不是

内存模型原理:

C++11 原子使用默认情况下 http://www.cplusplus.com/reference/atomic/atomic/store/ the 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

Here, i是 4。因为ptr是原子的,线程(2)安全地获取值&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是原子的。但这不适用于所指向的值。

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

Example:

// 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 ! 
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 中的原子指针以及在线程之间传递对象 的相关文章

随机推荐