在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
}
};发布于 2022-11-12 20:04:25
同时使用relaxed是不安全的。如果compare_exchange成功,则head将被更新为new_node值,其他读取head的线程将得到该指针。但是,如果没有发布顺序,写入new_node->next (现在的head->next)的值可能还不能全局可见,因此如果其他线程试图读取head->next,它可能会看到垃圾,或者以其他方式出现错误行为。
形式上,对new_node->next的写入需要在任何其他线程尝试读取它之前进行,这只能通过在存储区上进行发布命令来确保,该存储指示其他线程该值已就绪。(同样,读取head的线程需要使用获取排序。)通过对成功存储进行轻松的排序,在关系不存在之前就发生了,因此代码有一个数据竞争,其行为是未定义的。
对这两种情况都使用release是没有意义的,因为发布命令只对存储有意义,而在失败的情况下,不执行存储。事实上,由于这个原因,为故障排序传递std::memory_order_release实际上是非法的;这是在获取示例代码的页面上说明的。使用acquire或seq_cst是安全的(更强的排序总是安全的),但是没有必要,可能会造成不必要的性能损失。
https://stackoverflow.com/questions/74415931
复制相似问题