簡體   English   中英

在std :: atomic :: load的結果上使用Structure dereference( - >)運算符是否安全

[英]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 ab然后是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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM