简体   繁体   English

对原子结构和指针的误解

[英]Misunderstanding of atomic structs and pointers

My first question is: Is there any way to access the members of struct in an atomic<struct> object? 我的第一个问题是:有没有办法在atomic<struct>对象中访问struct的成员? For example, I get the compiler error: 例如,我得到编译器错误:

struct std::atomic<node>’ has no member named ‘data’ a.data = 0; 

in this segment 在这一部分

struct node{
  int data;
  node* next;
};

int main(){
  atomic<node> a;
  a.data = 0;
}

I can work around it by creating a temporary node like so: 我可以通过创建一个像这样的临时节点来解决它:

  atomic<node> a;
  node temp;
  temp.data = 0;
  a.store(temp);

but this doesn't seem very elegant. 但这似乎并不优雅。

The second question is, what if I have a pointer to an atomic object? 第二个问题是,如果我有一个指向原子对象的指针怎么办? Is there anyway to access the members of the node directly? 反正有没有直接访问节点的成员? Obviously the following does not compile, how would I change this to store 0 in the value of the node at b? 显然以下不编译,如何将其更改为在b的节点值中存储0?

atomic<node> b = new node;
b->data = 0;

This is a solution I've found, but again, is there a more elegant way of doing this?? 这是我发现的一个解决方案,但同样,有更优雅的方式吗?

atomic<node> *b;
node temp;
temp.data = 0;
b->store(&temp);

And lastly, what is the difference between atomic<node*> and atomic<node>* 最后, atomic<node*>atomic<node>*之间有什么区别?

this [workaround] doesn't seem very elegant. 这[解决方法]似乎并不优雅。

std::atomic<T> cannot make arbitrary operations atomic: only loading and storing the data is supported. std::atomic<T>不能使任意操作成为原子:只支持加载和存储数据。 That is why your "workaround" is actually the way to deal with atomic objects: you prepare the new node value in any way that you like, and then atomically set it into an atomic<node> variable. 这就是为什么你的“解决方法”实际上是处理原子对象的方法:你以任何你喜欢的方式准备新node值,然后将其原子地设置为atomic<node>变量。

what if I have a pointer to an atomic object? 如果我有一个指向原子对象的指针怎么办? Is there anyway to access the members of the node directly? 反正有没有直接访问节点的成员?

Accessing the content of a node through a pointer would not be atomic as well: since std::atomic<T> can guarantee only loading and storing its value to be atomic, it does not let you access T 's members without making an explicit copy. 通过指针访问节点的内容也不是原子的:因为std::atomic<T>只能保证加载和存储它的值是原子的,所以它不允许你在没有明确的情况下访问T的成员复制。 This is a good thing, because it prevents readers of the code from getting a false impression that the access to T 's internals is somehow atomic. 这是一件好事,因为它可以防止代码的读者得到错误的印象,即对T的内部的访问在某种程度上是原子的。

what is the difference between atomic<node*> and atomic<node>* atomic<node*>atomic<node>*之间有什么区别?

In the firs case, the atomic object stores a pointer, which can be accessed atomically (ie you can re-point this pointer to a new node atomically). 在第一种情况下,原子对象存储一个指针,该指针可以原子方式访问(即,您可以原子地将此指针重新指向新节点)。 In the second case, the atomic object stores the value that can be accessed atomically, meaning that you can read and write the entire node atomically. 在第二种情况下,原子对象存储可以原子方式访问的值,这意味着您可以原子地读取和写入整个node

When you do 当你这样做

atomic<node> a;
node temp; // use a.load() to copy all the fields of a to temp
temp.data = 0;
a.store(temp);

you loose the value of next field. 你失去了下一个领域的价值。 I'd make the suggested change. 我会做出建议的改变。 If node would have been a simple type , like std::atomic_int , I think that using the " = " operator would have been possible. 如果node是一个简单的类型 ,比如std :: atomic_int ,我认为使用“ = ”运算符是可能的。 Otherwise not. 否则不是。 I don't think there's another workaround for your case. 我不认为你的案子还有另一种解决方法。

And lastly, what is the difference between atomic < node* > and atomic < node > *? 最后,atomic <node *>和atomic <node> *之间有什么区别?

If you use atomic < node* > the operations done on the address of a node object will be atomic while in the other case you need to allocate memory for the atomic object and the operations done on the actual node object will be atomic. 如果使用atomic <node *>,则对节点对象的地址执行的操作将是原子操作,而在另一种情况下,您需要为原子对象分配内存,并且对实际节点对象执行的操作将是原子的。

Note that your "solution" includes a non-atomic read-modify-write of all the members other than .data . 请注意,您的“解决方案”包括除.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

If you want a struct where you can atomically update all members together or separately atomically modify one of them (without a compare_exchange_weak on the whole struct), you can use a union of an atomic struct and a struct with two atomic members . 如果你想要一个结构,你可以在其中原子地更新所有成员或单独原子地修改它们中的一个(整个结构上没有compare_exchange_weak ),你可以使用原子结构的联合和具有两个原子成员的结构 This might be useful for eg both pointers in a double-linked list, or a pointer + counter. 这对于例如双链表中的指针或指针+计数器可能是有用的。 Current compilers are bad at even reading one member of an atomic struct without doing something slow like using CMPXCHG16B to load the whole struct and then just look at one member. 当前的编译器甚至不会读取原子结构的一个成员,而不会像使用CMPXCHG16B加载整个结构那样慢,然后只看一个成员。 (That's the case on gcc6.2 even with memory_order_relaxed ). (即使使用memory_order_relaxed ,gcc6.2也是memory_order_relaxed )。

This union hack only works if you're using a C++ compiler that guarantees that writing one union member and then reading another is ok, like it is in C99. 只有在使用C ++编译器保证编写一个联合成员然后读取另一个联合成员时,这种联合黑客才有效,就像它在C99中一样。

This works for structs up to the max size the hardware can cmpxchg, ie 16B on x86-64 (if you enable -mcx16 in gcc, to use CMPXCHG16B which the first generation of K8 CPUs didn't support, so it's not technically baseline x86-64). 这适用于硬件可以cmpxchg最大尺寸的结构,即x86-64上的16B(如果在gcc中启用-mcx16 ,则使用第一代K8 CPU不支持的CMPXCHG16B,因此技术上不是基线x86 -64)。

For larger structs, atomic<the_whole_thing> won't be lock-free, and reading/writing to members of it through atomic<int> in another union member will not be safe. 对于较大的结构, atomic<the_whole_thing>将不是无锁的,并且通过另一个联合成员中的atomic<int>对其成员进行读/写将不安全。 Reading may still be ok, though. 但阅读可能还是可以的。

This may make a mess of the memory-ordering semantics, because even strongly-ordered x86 can reorder a narrow store with a wider load that fully contains it . 这可能会使内存排序语义变得混乱,因为即使是强排序的x86也可以对具有完全包含它的更宽负载的窄存储进行重新排序 If you mostly just need atomicity, it's great, but reading the full object (eg while doing a cmpxchg) in the same thread that just wrote one member requires an MFENCE on x86 even for acquire/release semantics. 如果你大多只需要原子性,这很好,但是在刚写入一个成员的同一个线程中读取完整对象(例如在执行cmpxchg时)需要在x86上使用MFENCE,即使是获取/释放语义。 You will always see your own store, but if other threads are storing to the same object, they can observe your store as happening after your load. 您将始终看到自己的商店,但如果其他线程存储到同一个对象,他们可以在加载后观察您的商店。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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