[英]Misunderstanding of atomic structs and pointers
我的第一个问题是:有没有办法在atomic<struct>
对象中访问struct的成员? 例如,我得到编译器错误:
struct std::atomic<node>’ has no member named ‘data’ a.data = 0;
在这一部分
struct node{
int data;
node* next;
};
int main(){
atomic<node> a;
a.data = 0;
}
我可以通过创建一个像这样的临时节点来解决它:
atomic<node> a;
node temp;
temp.data = 0;
a.store(temp);
但这似乎并不优雅。
第二个问题是,如果我有一个指向原子对象的指针怎么办? 反正有没有直接访问节点的成员? 显然以下不编译,如何将其更改为在b的节点值中存储0?
atomic<node> b = new node;
b->data = 0;
这是我发现的一个解决方案,但同样,有更优雅的方式吗?
atomic<node> *b;
node temp;
temp.data = 0;
b->store(&temp);
最后, atomic<node*>
和atomic<node>*
之间有什么区别?
这[解决方法]似乎并不优雅。
std::atomic<T>
不能使任意操作成为原子:只支持加载和存储数据。 这就是为什么你的“解决方法”实际上是处理原子对象的方法:你以任何你喜欢的方式准备新node
值,然后将其原子地设置为atomic<node>
变量。
如果我有一个指向原子对象的指针怎么办? 反正有没有直接访问节点的成员?
通过指针访问节点的内容也不是原子的:因为std::atomic<T>
只能保证加载和存储它的值是原子的,所以它不允许你在没有明确的情况下访问T
的成员复制。 这是一件好事,因为它可以防止代码的读者得到错误的印象,即对T
的内部的访问在某种程度上是原子的。
atomic<node*>
和atomic<node>*
之间有什么区别?
在第一种情况下,原子对象存储一个指针,该指针可以原子方式访问(即,您可以原子地将此指针重新指向新节点)。 在第二种情况下,原子对象存储可以原子方式访问的值,这意味着您可以原子地读取和写入整个node
。
当你这样做
atomic<node> a;
node temp; // use a.load() to copy all the fields of a to temp
temp.data = 0;
a.store(temp);
你失去了下一个领域的价值。 我会做出建议的改变。 如果node是一个简单的类型 ,比如std :: atomic_int ,我认为使用“ = ”运算符是可能的。 否则不是。 我不认为你的案子还有另一种解决方法。
最后,atomic <node *>和atomic <node> *之间有什么区别?
如果使用atomic <node *>,则对节点对象的地址执行的操作将是原子操作,而在另一种情况下,您需要为原子对象分配内存,并且对实际节点对象执行的操作将是原子的。
请注意,您的“解决方案”包括除.data
之外的所有成员的非原子读取 - 修改 - 写入。
atomic<node> a;
node temp = a.load();
temp.data = 0;
a.store(temp); // steps on any changes to other member that happened after our load
如果你想要一个结构,你可以在其中原子地更新所有成员或单独原子地修改它们中的一个(整个结构上没有compare_exchange_weak
),你可以使用原子结构的联合和具有两个原子成员的结构 。 这对于例如双链表中的指针或指针+计数器可能是有用的。 当前的编译器甚至不会读取原子结构的一个成员,而不会像使用CMPXCHG16B加载整个结构那样慢,然后只看一个成员。 (即使使用memory_order_relaxed
,gcc6.2也是memory_order_relaxed
)。
只有在使用C ++编译器保证编写一个联合成员然后读取另一个联合成员时,这种联合黑客才有效,就像它在C99中一样。
这适用于硬件可以cmpxchg最大尺寸的结构,即x86-64上的16B(如果在gcc中启用-mcx16
,则使用第一代K8 CPU不支持的CMPXCHG16B,因此技术上不是基线x86 -64)。
对于较大的结构, atomic<the_whole_thing>
将不是无锁的,并且通过另一个联合成员中的atomic<int>
对其成员进行读/写将不安全。 但阅读可能还是可以的。
这可能会使内存排序语义变得混乱,因为即使是强排序的x86也可以对具有完全包含它的更宽负载的窄存储进行重新排序 。 如果你大多只需要原子性,这很好,但是在刚写入一个成员的同一个线程中读取完整对象(例如在执行cmpxchg时)需要在x86上使用MFENCE,即使是获取/释放语义。 您将始终看到自己的商店,但如果其他线程存储到同一个对象,他们可以在加载后观察您的商店。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.