C++中的原子指针和线程之间传递对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26787086/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Atomic pointers in c++ and passing objects between threads
提问by Michael
My question involves std::atomic and the data that this pointer points to. If in thread 1 I have
我的问题涉及 std::atomic 和该指针指向的数据。如果在线程 1 我有
Object A;
std:atomic<Object*> ptr;
int bar = 2;
A.foo = 4; //foo is an int;
ptr.store(*A);
and if in thread 2 I observe that ptr points to A, can I be guaranteed that ptr->foo is 4 and bar is 2? Does the default memory model for the atomic pointer (Sequentially consistent) guarantee that assignments on non atomic (in this case A.foo) that happen before an atomic store will be seen by other threads before it sees the assignment of the same atomic.store for both cases?
如果在线程 2 中我观察到 ptr 指向 A,我能否保证 ptr->foo 是 4 而 bar 是 2?原子指针的默认内存模型(顺序一致)是否保证在原子存储之前发生的非原子(在这种情况下为 A.foo)上的分配将被其他线程看到,然后才能看到相同的 atomic.store 的分配对于这两种情况?
If it helps or matters, I am using x64 (and I only care about this platform), gcc (with a version that supports atomics).
如果它有帮助或重要,我正在使用 x64(我只关心这个平台),gcc(具有支持原子的版本)。
回答by Christophe
The answer is yes and perhaps no
答案是肯定的,也许不是
The memory model principles:
内存模型原理:
C++11 atomics use by defaultthe std::memory_order_seq_cst
memory ordering, which means that operations are sequentially consistent.
C++11 原子默认使用std::memory_order_seq_cst
内存排序,这意味着操作是顺序一致的。
The semantics of this is that ordering of all operations are as if all these operations were performed sequentially :
它的语义是所有操作的排序就好像所有这些操作都是按顺序执行的:
C++ standard section 29.3/3 explains how this works for atomics: "There shall be a single total order S on all memory_order_seq_cst operations, consistent with the “happens before”order and modification orders for all affected locations, such that each memory_order_seq_cst operation that loads a value observes either the last preceding modification according to this order S, or the result of an operation that is not memory_order_seq_cst."
The section 1.10/5 explains how this impacts also non-atomics: "The library defines a number of atomic operations (...) that are specially identified as synchronization operations. These operations play a special role in making assignments in one thread visible to another."
C++ 标准第 29.3/3 节解释了这如何适用于原子:“所有 memory_order_seq_cst 操作上都应有一个总顺序 S,与所有受影响位置的“发生之前”顺序和修改顺序一致,这样加载的每个 memory_order_seq_cst 操作一个值要么根据此顺序 S 观察最后一次前面的修改,要么观察不是 memory_order_seq_cst 的操作的结果。”
1.10/5 节解释了这如何影响非原子:“库定义了许多原子操作(...),它们被特别标识为同步操作。这些操作在使一个线程中的赋值对另一个。”
The answer to your question is yes !
你的问题的答案是肯定的!
Risk with non-atomic data
非原子数据的风险
You shall however be aware that in reality the consistency guarantee is more limited for the non-atomic values.
但是,您应该意识到,实际上,对于非原子值,一致性保证更为有限。
Suppose a first execution scenario:
假设第一个执行场景:
(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
is 4. Because ptr
is atomic, thread (2) safely gets the value &A
when it reads the pointer. The memory ordering ensures that all assignments made BEFORE ptr
are seen by the other threads ("happens before" constraint).
这里,i
是 4。因为ptr
是原子的,线程 (2)&A
在读取指针时安全地获取该值。内存排序确保所有在 BEFORE 之前ptr
进行的分配都被其他线程看到(“发生在”约束之前)。
But suppose a second execution scenario:
但假设第二个执行场景:
(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
Here the result is undefined. It could be 4 because of the memory ordering guaranteed that what happens before the ptr
assignement is seen by the other threads. But nothing prevents assignments made afterwards to be seen as well. So it could be 8.
这里的结果是未定义的。它可能是 4,因为内存排序保证ptr
了其他线程可以看到分配之前发生的事情。但没有什么能阻止事后进行的分配也能被看到。所以可能是8。
If you would have had *ptr = 8;
instead of A.foo=8;
then you would have certainty again: i
would be 8.
如果你有*ptr = 8;
而不是A.foo=8;
那么你会再次确定:i
将是 8。
You can verify this experimentally with this for example:
您可以通过实验验证这一点,例如:
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;
}
Conclusions
结论
To conclude, the answer to your question is yes, but only if no other change to the non atomic data happens after the synchronisation. The main risk is that only ptr
is atomic. But this does not apply to the values pointed to.
总而言之,您的问题的答案是肯定的,但前提是同步后非原子数据没有发生其他更改。主要的风险是只有ptr
是原子的。但这不适用于指向的值。
To be noted that especially pointers bring further synchronisation risk when you reassign the atomic pointer to a non atomic pointer.
需要注意的是,当您将原子指针重新分配给非原子指针时,尤其是指针会带来进一步的同步风险。
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 !