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
的分配之前被其他线程看到?
如果有帮助或重要的话,我正在使用x64(我只关心这个平台)、gcc(支持原子的版本)。
答案是肯定的,也许不是
内存模型原理:
C++11原子在默认情况下使用std::memory_order_seq_cst
内存排序,这意味着操作是顺序一致的。
其语义是,所有操作的顺序就好像所有这些操作都是按顺序执行的:
-
C++标准第29.3/3节解释了这对于原子学是如何工作的:"所有memory_order_seq_cst操作都应有一个单一的总顺序S,与所有受影响位置的"之前发生"顺序和修改顺序一致,这样每个memory_orden_seq_ct加载值的操作观察到根据该顺序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原子操作具有获取/释放语义。
因此,看到你的商店的线程也会看到在它之前执行的所有操作
你可以在这里找到更多的细节。
- 在cuda线程之间共享大量常量数据
- 线程之间的布尔停止信号
- 线程之间的实时数据共享
- 线程之间的通信不起作用 - C++
- 简单使用 std::atomic 在两个线程之间共享数据
- 不同线程之间的互斥锁同步
- 多个线程之间的获取-释放内存顺序
- 如何在两组线程之间正确来回传输控制权
- 如果两个线程调用同一个函数,但函数中的所有变量都是局部变量,我还需要担心线程之间共享数据吗?
- 填充和保存线程之间的共享缓冲区
- 在C 中共享线程之间的列表
- 在 API 和应用程序线程之间共享数据
- 将线程之间的标准输入/输出重定向
- Boost::线程如何在主线程和工作线程之间同步
- std::线程之间的互斥体同步
- C++ - 多线程 - 线程之间的通信
- Qthread char阵列在两个线程之间传递后被摧毁
- 线程之间类的静态数据共享
- 增强ASIO和线程之间的消息传递
- 将数组分配在固定数量的线程之间