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