繁体   English   中英

对原子结构和指针的误解

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM