c++ - C++ memory_order_acquire/release 问题
问题描述
memory_order_acquire
最近学习了c++的六种内存命令,对and感到很困惑memory_order_release
,下面是来自cpp的一个例子:
#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_seq_cst); }
void write_y() { y.store(true, std::memory_order_seq_cst); }
void read_x_then_y() {
while (!x.load(std::memory_order_seq_cst));
if (y.load(std::memory_order_seq_cst))
++z;
}
void read_y_then_x() {
while (!y.load(std::memory_order_seq_cst));
if (x.load(std::memory_order_seq_cst))
++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); // will never happen
}
在 cpp 参考页面中,它说:
此示例演示了需要顺序排序的情况。
任何其他排序都可能触发断言,因为线程 c 和 d 可能以相反的顺序观察原子 x 和 y 的变化。
所以我的问题是为什么不能在这里使用memory_order_acquire和memory_order_release ?memory_order_acquire 和 memory_order_release 提供什么语义?
一些参考资料: https ://en.cppreference.com/w/cpp/atomic/memory_order https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync
解决方案
顺序一致性提供了所有顺序一致操作的单一总顺序。因此,如果您在线程 A 中有一个顺序一致的存储,并且在线程 B 中有一个顺序一致的加载,并且存储在加载之前排序(按所述单个总顺序),那么 B 会观察 A 存储的值。所以基本上是顺序一致性保证存储对其他线程“立即可见”。发行商店不提供此保证。
正如 Peter Cordes 正确指出的那样,“立即可见”一词相当不精确。“可见性”源于这样一个事实,即所有 seq-cst 操作都是完全有序的,并且所有线程都遵守该顺序。由于存储和加载是完全排序的,因此在执行后续加载(以单个总顺序)之前,存储的值变得可见。
不同线程中的获取/释放操作之间不存在这样的总顺序,因此不存在可见性保证。只有在获取操作观察到释放操作的值时,操作才会被排序,但不能保证释放操作的值何时对执行获取操作的线程可见。
让我们考虑一下如果我们在这个例子中使用获取/释放会发生什么:
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); // can actually happen!!
}
由于我们无法保证可见性,因此线程可能会c
观察到x == true
and y == false
,而同时线程d
可以观察到y == true
and x == false
。所以两个线程都不会增加z
并且断言会触发。
有关 C++ 内存模型的更多详细信息,我可以推荐我合着的这篇论文:C/C++ 程序员的内存模型
推荐阅读
- python - Python xml2dict 类型取决于出现次数
- javascript - 当我在 JavaScript 上的对象上按下鼠标时更改鼠标光标
- node.js - 将带有反应路由的 create-react-app 部署到 cpanel
- flutter - Flutter:名为“read”的成员在扩展“ReadContext”和“BuildContextX”中定义,两者都不是更具体的
- python - 在嵌入式 Flinkrunner (apache_beam [GCP]) 中使用 pub/sub io 运行光束流管道 (Python) 时出错
- django - 自定义 Django 管理员搜索
- jquery - 为什么这个函数不会将页面加载到 div 中?(jQuery AJAX)
- c# - Visual Studio 2019 中的 SQL Server 数据库
- c++ - 如何在 vs 代码“C++”中安装和运行 boost 库
- c++ - U 没有命名类型 - 模板编译错误