获取/发布与 C++11 中的顺序一致性?


#include <thread>
#include <atomic>
#include <cassert>
std::atomic<bool> x = {false};
std::atomic<bool> y = {false};
std::atomic<int> z = {0};
void write_x()
{
x.store(true, std::memory_order_release);
}
void write_y()
{
y.store(true, std::memory_order_release);
}
void read_x_then_y()
{
while (!x.load(std::memory_order_acquire))
;
if (y.load(std::memory_order_acquire)) {
++z;
}
}
void read_y_then_x()
{
while (!y.load(std::memory_order_acquire))
;
if (x.load(std::memory_order_acquire)) {
++z;
}
}
int main()
{
std::thread a(write_x);
std::thread b(write_y);
std::thread c(read_x_then_y);
std::thread d(read_y_then_x);
a.join(); b.join(); c.join(); d.join();
assert(z.load() != 0);
}

如果我在 cppreference 的最后一个示例中seq_cst获取/发布,assert(z.load() != 0)会失败吗?

  • Seq_CST可以阻止 StoreLoad 重新排序,但代码没有。
  • 获取可以防止负载重新排序。
  • 发布可以防止商店商店重新排序。

是的,如果您像以前一样使用acquire/release顺序,则可能会z.load() == 0代码中。独立写入xy之间没有先行关系。cpp偏好使用这个例子专门说明获取/释放是不够的情况,这并非巧合。

这有时被称为IRIW(独立写入的独立读取(,并且在某些硬件订购模型中往往被掩盖。特别是,仅根据可能的加载-加载、加载-存储、存储-存储等重新排序定义的内存模型并不能真正说明 IRIW。在 x86 内存模型中,不允许 IRIW 重新排序,因为有一个子句解释说,存储具有总顺序,并且所有处理器都以相同的顺序查看存储。

我不知道在使用获取和释放所需的屏障和/或指令时,是否有任何常用的 CPU 允许 IRIW 重新排序,但如果有人这样做,我不会感到惊讶。

是的,断言可以触发。

获取/释放不保证的主要属性是单个总修改顺序。它仅保证ab的先前动作(不存在(被cd观察到,如果他们从负载中看到true

一个(略微人为的(示例是在未完全缓存一致的多 CPU(物理套接字(系统上。模具 1 具有核心 A 运行线程a和核心 C 运行线程c。模具 2 有核心 B 运行线程b和核心 D 运行线程d。与命中片上缓存的内存操作相比,两个套接字之间的互连具有较长的延迟。

ab在同一挂钟时间运行。C 与 A 在片上,因此可以看到存储立即x,但互连延迟了它对存储的观察以y,因此它看到了旧值。类似地,D与B在芯片上,所以它看到商店要y,但错过了商店x

而如果您具有顺序一致性,则需要一些协调来强制执行总顺序,例如"在互连同步缓存时阻止 C 和 D"。

最新更新