C ++中的原子指针和线程之间的传递对象 [英] Atomic pointers in c++ and passing objects between threads

查看:279
本文介绍了C ++中的原子指针和线程之间的传递对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题涉及std :: atomic和此指针指向的数据.如果在线程1中,我有

My question involves std::atomic and the data that this pointer points to. If in thread 1 I have

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)上的非原子分配会在看到相同原子.store的分配之前被其他线程看到.对于这两种情况?

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?

如果有帮助或重要,我正在使用x64(并且我只关心此平台),gcc(具有支持原子的版本).

If it helps or matters, I am using x64 (and I only care about this platform), gcc (with a version that supports atomics).

推荐答案

答案是肯定的,也许不是

The answer is yes and perhaps no

内存模型原理:

C ++ 11原子默认使用 排序,表示操作顺序一致.

C++11 atomics use by default the std::memory_order_seq_cst memory ordering, which means that operations are sequentially consistent.

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

The semantics of this is that ordering of all operations are as if all these operations were performed sequentially :

  • C ++标准第29.3/3节解释了此方法对 atomics 的工作原理:"在所有memory_order_seq_cst操作上应有一个总和S,与一致""发生在所有受影响位置的命令和修改命令之前,这样,每个memory_order_seq_cst 加载值的操作遵循此顺序S观察到最后一个先前的修改,或者遵循不是memory_order_seq_cst的操作的结果."

  • 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."

1.10/5部分说明了这对非原子的影响:"该库定义了许多原子操作(...),这些操作被专门标识为同步这些操作在使一个线程中的分配对另一线程可见时起着特殊的作用."

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."

您的问题的答案是肯定的!

The answer to your question is yes !

包含非原子数据的风险

但是,您应该意识到,实际上对于非原子值的一致性保证更为有限.

You shall however be aware that in reality the consistency guarantee is more limited for the non-atomic values.

假设第一个执行方案:

(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之前所做的所有分配(发生在……之前"约束).

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).

但假设有第二种执行方案:

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

这里的结果是不确定的.由于保证了内存顺序,其他线程可以看到ptr分配之前发生的情况,因此可能为4.但是,也没有什么可以阻止以后所做的作业被看到.因此可能是8.

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.

如果您使用的是*ptr = 8;而不是A.foo=8;,那么您将再次确定:i将是8.

If you would have had *ptr = 8; instead of A.foo=8; then you would have certainty again: i would be 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;
}

结论

总而言之,您的问题的答案是是",但前提是在同步后未对非原子数据进行任何其他更改.主要风险是只有ptr是原子的.但这不适用于所指向的值.

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.

要注意的是,当您将原子指针重新分配给非原子指针时,尤其是指针带来了进一步的同步风险.

To be noted that especially pointers bring further synchronisation risk when you reassign the atomic pointer to a non atomic pointer.

示例:

// 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 ++中的原子指针和线程之间的传递对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆