簡體   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