[英]Is it safe to use the Structure dereference(->) operator on the result of std::atomic::load
在嘗試使用std原子指針時,我碰到了以下內容。 說我這樣做:
std::atomic<std::string*> myString;
// <do fancy stuff with the string... also on other threads>
//A can I do this?
myString.load()->size()
//B can I do this?
char myFifthChar = *(myString.load()->c_str() + 5);
//C can I do this?
char myCharArray[255];
strcpy(myCharArray, myString.load()->c_str());
我很確定C是非法的,因為myString可能會在此期間被刪除。
但是我不確定A和B.我認為它們是非法的,因為在執行讀操作時指針可能會被引用。
但是,如果是這種情況,您怎么能從可能被刪除的原子指針中讀取。 由於負載是1步,並且數據的讀取是1步。
// A can I do this?
myString.load()->size()
是的,你可以 ,但是你確實有一個競爭條件,如果其他東西可能正在變異或破壞/解除分配你收到的myString
快照所指向的string
。 換句話說,原子檢索指針后的情況與多個線程可能有指針的任何std::string
對象相同,除了......
存在這樣的問題:原子load
是否保證對string
某些特定構造/更改 - 可能由更新myString
指向您load
指針的特定string
實例的任何線程執行 - 將對您可見。 默認設置是為了確保這一點,但您可能希望將memory_order
參數的這種解釋讀入load()
。 請注意, 沒有明確要求內存同步並不能保證您不會被其他線程變更/破壞。
所以,假設myString()
連續指向string
a
, b
然后是c
,並且你的代碼檢索&b
...只要你調用size()
string
b
沒有被變異或被破壞/解除分配,你沒事。 在調用b
的.size()
之前/期間/之后, myString()
可能會更新為指向c
並不重要。
退一步,程序知道在調用load()
后多長時間你可能會嘗試取消引用指針,如果b
對象稍后被變異或被破壞/解除分配,那么調用你可能會很棘手。建議不會圍繞后來的突變/破壞進行任何同步。 顯然,你可以用無數種方式添加這種協調(例如一些其他原子計數器/標志,使用條件變量通知可能的修改器/析構函數/刪除器......),或者你可能有時決定接受這樣的競爭條件(例如也許如果已知b
是一個寬大的LRU緩存中的最新條目之一)。
如果你正在做一些像圍繞許多static const string
實例循環myString
事情,你不必擔心上面的所有變異/破壞(除非你在main()
之前/之后訪問它們) 。
// B can I do this?
char myFifthChar = *(myString.load()->c_str() + 5);
是的,上述所有警告。
// C can I do this?
char myCharArray[255];
strcpy(myCharArray, myString.load()->c_str());
是的,如上所述(並且提供的緩沖區足夠大)。
我很確定C是非法的,因為myString可能會在此期間被刪除。
如上所述 - 該問題對於您提到的所有3種用途同樣有效,對C來說更有可能,因為復制需要更多的CPU周期來完成,而不是讓垃圾值失去競爭可能導致緩沖區溢出。
我很確定C是非法的,因為myString可能會在此期間被刪除。
所有例子都是如此。 由於原子負載而唯一安全的是負載本身 - 僅此而已。 您有責任確保任何后續操作的安全性。 在這種情況下,沒有,所以它非常不安全。
從原子指針加載的唯一方法是確保你擁有結果 - 如std::shared_ptr<T>
,或者保證它可以存活一段更長的壽命並且你應該禁止所有寫入。
如果另一個線程可能修改或刪除string
對象,則所有這些都是非法的。
使用atomic
同步訪問指針,但是你沒有做任何事情來同步對它所指向的對象的訪問。
有人提到你的方法是有風險的業務。 您可能需要考慮以下內容:將std::shared_ptr<const std::string>
與不可變值以及shared_ptr
atomic_load和atomic_store一起使用 。 std::shared_ptr
將確保您不訪問懸空指針,而不變性(字符串在構造后不會更改)將保證對字符串本身的訪問是線程安全的,因為標准定義的所有const
方法都是線程安全的。
編輯:根據要求解釋“風險業務”的含義:如果使用std::atomic<std::string *>
,則很容易意外引入競爭條件,例如:
// Data
std::atomic<std::string *> str(new std::string("foo"));
// Thread 1
std::cout << *str.load();
// Thread 2
*str.load() = "bar"; // race condition with read access in thread 1
// Thread 2 (another attempt using immutable instances)
auto newStr = new std::string("bar");
auto oldStr = str.exchange(newStr);
delete oldStr; /* race condition with read access in thread 1
because thread 1 may have performed load() before
the exchange became visible to it, and may not
be finished using the old object. */
請注意,這與operator <<
無關,即使只是在線程1中的字符串上調用size()
也會導致競爭條件。
在實踐中,人們可能會看到“修復”,例如在更新中使用不可變字符串在delete
之前添加sleep
,以便線程1有足夠的時間用舊指針完成其業務。 雖然這在大多數情況下可能在特定實現中起作用,但它不會引入真正的排序(在C ++標准中發生之前的關系),因此不是正確的rsp。 便攜式方案。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.