在 CPP 參考的代碼片段中,記憶體屏障std::memory_order_release和 std::memory_order_relaxed分別用于成功和失敗的情況。什么時候std::memory_order_release兩者都可以使用std::memory_order_relaxed?
template<class T>
struct node
{
T data;
node* next;
node(const T& data) : data(data), next(nullptr) {}
};
template<class T>
class stack
{
std::atomic<node<T>*> head;
public:
void push(const T& data)
{
node<T>* new_node = new node<T>(data);
// put the current value of head into new_node->next
new_node->next = head.load(std::memory_order_relaxed);
// now make new_node the new head, but if the head
// is no longer what's stored in new_node->next
// (some other thread must have inserted a node just now)
// then put that new head into new_node->next and try again
while(!std::atomic_compare_exchange_weak_explicit(
&head,
&new_node->next,
new_node,
std::memory_order_release,
std::memory_order_relaxed))
; // the body of the loop is empty
// note: the above loop is not thread-safe in at least
// GCC prior to 4.8.3 (bug 60272), clang prior to 2014-05-05 (bug 18899)
// MSVC prior to 2014-03-17 (bug 819819). See member function version for workaround
}
};
uj5u.com熱心網友回復:
兩者都使用relaxed是不安全的。如果compare_exchange成功,則head使用 的值更新new_node,其他讀取的執行緒head將獲得該指標。但是,如果沒有發布順序,寫入new_node->next(now head->next) 的值可能還不是全域可見的,因此如果其他執行緒嘗試讀取head->next它可能會看到垃圾,或者以其他方式行為不端。
正式地,寫入new_node->next需要在任何其他執行緒嘗試讀取它之前發生,這只能通過在存盤上具有釋放順序來確保其他執行緒該值已準備好。(同樣,讀取的執行緒head需要使用獲取排序。)在成功存盤上放松排序,不存在發生之前的關系,因此代碼存在資料競爭并且其行為未定義。
兩者都使用release是沒有意義的,因為發布排序只對存盤有意義,并且在失敗的情況下,不執行存盤。事實上,由于這個原因,通過std::memory_order_release失敗排序實際上是非法的;這在您從中獲取示例代碼的頁面上進行了說明。使用acquireorseq_cst會是安全的(更強的排序總是安全的)但沒有必要,并且可能會導致不必要的性能損失。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/532660.html
標籤:c 11原子记忆障碍
上一篇:使用回圈和物件的密碼驗證器
