获取负载是否可以与其他获取操作一起重新排序? cppreference 表示只有非原子的和松弛的是通过获取排序的

Can acquire loads reorder with other acquire operations? cppreference says only non-atomic and relaxed are ordered by acquire

根据C++ Referencemutex.lock()是一个memory_order_acquire操作,mutex.unlock()是一个memory_order_release操作。

但是,memory_order_acquirememory_order_release只对非原子和宽松的原子操作有效。

memory_order: Release-Acquire ordering on cppreference

If an atomic store in thread A is tagged memory_order_release and an atomic load in thread B from the same variable is tagged memory_order_acquire, all memory writes (non-atomic and relaxed atomic) that happened-before the atomic store from the point of view of thread A, become visible side-effects in thread B

C++中的mutex能否保证原子操作的可见性?一个例子如下。代码 A 能否在 mu.lock() 之前重新排序,并且线程 bx 读取为 false


#include <thread>
#include <atomic>
#include <cassert>
#include <iostream>
#include <unistd.h>

std::atomic<bool> x = {false};
std::mutex mu;

void write_x(){
  mu.lock();
  std::cout << "write_x" << std::endl;
  x.store(true, std::memory_order_release);
  mu.unlock();
}

void read_x() {
  mu.lock();
  std::cout << "read_x" << std::endl;
  assert(x.load(std::memory_order_acquire)); // A
  mu.unlock();
}

int main() {
  std::thread a(write_x);
  usleep(1);
  std::thread b(read_x);

  a.join(); b.join();

  return 0;
}

TL:DR:“所有内存写入”的意思是全部,而不是只是提到的种类,但措辞令人困惑。可能只是想指出 even non-atomic 和宽松的原子操作在 synchronizes-with 中是安全可见的,但措辞中缺少“包括”一词。


请注意,cppreference 是一个旨在解释 标准的 wiki。 它不是规范的技术语言,有时甚至会解释一些东西与 ISO C++ 标准不同的术语。

它通常非常 好,但不要在某些事情看起来很奇怪时就认为它是完美的。从周围的上下文(和理智)来看,就像段落中的最后一句话没有任何限制地说“一切”,这仍然是相当明显的意思。


ISO C++ 很清楚。 “看到”释放操作的获取操作创建 synchronizes-with 关系。 发布前的所有内容在获取操作后对代码可见。

因此,就访问全局一致共享内存状态的操作的模型而言,获取操作会阻止 所有内容 在它们之前重新排序。包括发布和 seq_cst 操作。 (请注意,cppreference 的这一部分没有提及 reordering,只是为了保证可见性或不可见性。访问全局一致状态的本地重新排序实际上是真实 CPU 的工作方式,所以这样描述事情通常更方便,就像你在问题中所做的那样。)

这意味着 C++ 的获取和释放定义符合标准术语,没有疯狂的魔法异常。 https://preshing.com/20120913/acquire-and-release-semantics/


请注意,有些人使用“松散原子”来描述所有弱于 seq_cst 的顺序。示例:Herb Sutter 在 this question 的演讲中就是这样使用它的。

这可能就是 cppreference 定义中的意思,但我不知道他们为什么要排除 seq_cst。所有原子操作和 non-atomic 操作都是有序的。所以也许他们的意思是mo_relaxed,只是想指出甚至那些是有序的/可见的。

(seq_cst 可以说已经订购了 本身 wrt. everything else, 所以“当然”订购了关于获取和释放操作。但这个原因似乎不太可能。)


如果是为了强调它也订购了较弱的订单,他们应该写“including non-atomic and放松的原子。如果没有“包括”一词,该措辞可以理解为暗示 non-atomic 和 relaxed-atomic。只有了解大局,什么是理智,什么是理智,才能给你正确的阅读。

需要准确理解的技术写作,经常会用到“包括但不限于”这句话。


另请注意,您的示例仍然可以触发断言,只是不是因为您担心的原因。

如果线程 a 启动缓慢,线程 b 可以先进入其临界区并在另一个线程中的打印+存储发生之前打印+读取 x

编写这样的玩具示例的通常方法是一个循环,该循环在获取负载上旋转,直到它看到一个值,例如像 data_read 这样的标志由释放操作存储在您关心的存储之后。这样你就知道读取端在获取操作之后运行,synced-with 写入端的释放操作。