繁体   English   中英

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

[英]Atomic pointers in c++ and passing objects between threads

我的问题涉及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是 2 吗?

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

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

答案是肯定的,也许不是

内存模型原理:

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

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

  • C++ 标准第 29.3/3 节解释了这如何适用于原子:“所有 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) 在读取指针时安全地获取值&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 ! 

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

所以看到你的商店的线程也会看到在它之前执行的所有操作。

您可以在此处找到更多详细信息。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM