答案是肯定的,也许不是
内存模型原理:
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) 在读取指针时安全地获取值 &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 !