为什么在 CAS 循环失败时首选 std::memory_order_relaxed ?

Dea*_*Seo 6 c++ multithreading memory-model compare-and-swap stdatomic

当谈到使用CAS 循环std::atomic实现时,此链接中的 cppreference给出了以下示例push

template<typename T>
class stack
{
    std::atomic<node<T>*> head;
 public:
    void push(const T& data)
    {
      node<T>* new_node = new node<T>(data);
      new_node->next = head.load(std::memory_order_relaxed);

      while(!head.compare_exchange_weak(new_node->next, new_node,
                                        std::memory_order_release,
                                        std::memory_order_relaxed /* Eh? */));
    }
};
Run Code Online (Sandbox Code Playgroud)

现在,我不明白如何将 comestd::memory_order_relaxed用于失败情况,因为据我了解, (与-strongcompare_exchange_weak相同,但为了方便起见,我只是使用弱版本)是失败时的加载操作,这意味着它从另一个线程中成功的 CAS 操作加载,因此它应该使用同步std::memory_order_releasestd::memory_order_acquire

while(!head.compare_exchange_weak(new_node->next, new_node,
                                  std::memory_order_release,
                                  std::memory_order_acquire /* There you go! */));
Run Code Online (Sandbox Code Playgroud)

假设,如果“宽松负载”获得旧值之一,最终一次又一次失败,并在循环中停留额外的时间怎么办?

下面这张粗糙的图片是我的大脑被卡住的地方。

在此输入图像描述

T2 的商店在 T1 上不应该可见吗?(通过相互之间具有同步关系)

总结一下我的问题,

  • 为什么不呢std::memory_order_acquire,而不是std::memory_order_relaxed失败呢?
  • 什么才算std::memory_order_relaxed足够呢?
  • std::memory_order_relaxed失败是否意味着(可能)更多循环?
  • 同样,std::memory_order_acquire失败是否意味着(可能)更少的循环?(除了性能上的缺点)

cur*_*guy 0

我不明白为什么 std::memory_order_relaxed 用于失败

我不明白你如何抱怨该失败分支上缺乏获取语义,但又不抱怨

head.load(std::memory_order_relaxed);

进而

while(!head.compare_exchange_weak(new_node->next, new_node,
                                  std::memory_order_release
Run Code Online (Sandbox Code Playgroud)

两者都没有“与”您未向我们展示的其他操作“同步”的获取操作。您关心的其他操作是什么?

如果该操作很重要,请显示该操作并告诉用户该代码如何依赖于该其他操作的“发布”(或“我完成”信号)。

答案:push函数绝不依赖于其他函数发布的任何“我完成了”信号,因为推送不使用其他已发布的数据,不读取其他推送的元素等。

为什么失败时不使用 std::memory_order_acquire 而是使用 std::memory_order_relaxed ?

为了获得什么?换句话说,要观察什么成就?

失败时 std::memory_order_relaxed 是否意味着(可能)更多循环?

不会。故障模式与内存可见性无关;这是CPU缓存机制的一个功能。

编辑:

我刚刚看到你图片中的文字:

T2 的商店在 T1 上不应该可见吗?(通过相互之间具有同步关系)

实际上,您误解了同步:它不会传播正在读取的原子变量的值,因为根据定义,原子是可与竞争条件一起使用的原语。原子的读取始终返回原子变量的值,如由其他线程(或同一线程)写入的那样。如果不是这种情况,那么原子操作就没有意义。

读取单个原子变量不需要内存排序。