为何使用C++ seq_cst内存模型时程序出现不符合预期的结果?
背景与原示例
我从某文档的acquire/release章节取出示例并改写为bool版本:
-Thread 1- y.store (true, memory_order_release); -Thread 2- x.store (true, memory_order_release); -Thread 3- assert (y.load (memory_order_acquire) == true && x.load (memory_order_acquire) == false) -Thread 4- assert (y.load (memory_order_acquire) == false && x.load (memory_order_acquire) == true)
该文档指出:
如果改用顺序一致(sequentially consistent)模型,那么两个存储操作中必然有一个先发生于(happen-before)另一个(具体顺序在运行时确定),线程间的值会同步,因此如果其中一个断言成立,另一个断言必然失败。
我按照这个逻辑,用std::memory_order_seq_cst实现了如下C++代码,但程序偶尔会输出"success!!! iteration number: ",也就是x_before_y和y_before_x同时为true,这和文档描述不符,请问原因是什么?
#include <atomic> #include <thread> #include <vector> #include <iostream> bool test() { std::atomic_bool is_started{false}; std::atomic_bool x{false}; std::atomic_bool y{false}; std::atomic_bool x_before_y{false}; // 标记store(x, true)先于store(y, true) std::atomic_bool y_before_x{false}; // 标记store(y, true)先于store(x, true) std::thread t1([&]{ // -Thread 1- while (!is_started.load()) {} y.store(true, std::memory_order_seq_cst); // store(y, true) }); std::thread t2([&]{ // -Thread 2- while (!is_started.load()) {} x.store(true, std::memory_order_seq_cst); // store(x, true) }); std::thread t3([&] { // -Thread 3- while (!is_started.load()) {} if (!y.load(std::memory_order_seq_cst) && x.load(std::memory_order_seq_cst)) { x_before_y.store(true); } }); std::thread t4([&]{ // -Thread 4- while (!is_started.load()) {} if (y.load(std::memory_order_seq_cst) && !x.load(std::memory_order_seq_cst)) { y_before_x.store(true); } }); is_started.store(true); t1.join(); t2.join(); t3.join(); t4.join(); return x_before_y.exchange(false) && y_before_x.exchange(false); } int main() { for (int i = 0; i < 100000000; i++) { if (test()){ std::cout << "success!!! iteration number: " << i << std::endl; return 1; } if (i % 1000000 == 0) { std::cout << "still running" << std::endl; } } std::cout << "fail :(" << std::endl; return 0; }
原因分析
1. 代码逻辑与原文档断言的核心意图不符
原文档的断言是验证同一全局状态下的两个值组合:要么y为true且x为false,要么y为false且x为true。但你的代码中,t3和t4是独立异步执行的,它们的两次读取(x和y)并非针对同一个全局状态:
- t3可能先读取y为false、x为true,设置
x_before_y;之后t4在t1完成y的存储后,读取y为true,同时t2的x存储还没被t4观测到,读取x为false,设置y_before_x。 - 这两个线程的观察发生在不同时间点,组合起来就会出现两个标记同时为true的情况,完全偏离了原示例“同一状态下两个断言互斥”的核心逻辑。
2. seq_cst的全局总序不代表所有线程同时看到同一状态
顺序一致模型保证所有seq_cst操作存在一个全局的执行顺序,但每个线程读取seq_cst变量时,是在自身执行流中看到这个总序的某个阶段:
- 假设全局总序是
t2的x.store先于t1的y.store,但t4可能在t2的x.store完成前就读取x(得到false),之后读取y时t1的y.store已完成(得到true);而t3可能在t2的x.store完成后读取x(得到true),在t1的y.store完成前读取y(得到false)。 - 这种情况下,两个线程分别观察到了全局总序的不同切片,自然会同时触发两个标记。
3. 错误映射了“happen-before”的判断逻辑
你用x_before_y和y_before_x标记的是“某个线程观察到的状态”,而非两个存储操作实际的happen-before关系。即使两个存储操作存在明确的全局顺序,线程的异步观察也可能捕捉到中间状态,导致错误的标记。
修正方向
要准确模拟原文档的逻辑,需确保每个线程对x和y的读取是一个连贯的状态快照,并且验证的是“同一状态下两个断言互斥”。例如,可以让单个线程依次检查两种断言条件,或者让t3和t4在读取x和y后,仅当组合状态符合断言时才标记,同时避免异步观察的交叉干扰。
内容的提问来源于stack exchange,提问作者Alex

