我的问题涉及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 原子使用 by default 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
assignement 之前发生的事情被其他线程看到。但没有什么能阻止事后所做的分配也被看到。所以它可能是 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++ - C++中的原子指针和线程间传递对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26787086/