C++标准下原子对象修改顺序与执行时序逆序的合规性问询
时间线上后执行的原子操作能否在修改顺序中先于先执行的操作?
问题描述
如果时间线上后执行的原子操作,在原子对象的修改顺序中反而先于先执行的操作,这样的C++实现是否符合标准?
初始示例
考虑以下代码:
#include <iostream> #include <atomic> #include <thread> #include <chrono> int main(){ std::atomic<int> v = 0; std::thread t1([&](){ if(v.exchange(1,std::memory_order::acq_rel) == 2){ // #1 std::cout<<"t1\n"; // #2 } }); std::this_thread::sleep_for(std::chrono::seconds(24 * 60 * 60)); // #3 std::thread t2([&](){ if(v.exchange(2,std::memory_order::acq_rel) == 0){ // #4 std::cout<<"t2\n"; // #5 } }); t1.join(); t2.join(); }
代码中#3的延迟让#4在物理时间上远晚于#1执行。从C++标准角度,是否允许出现以下两种输出?
第一种输出:
t1 t2
即先打印t1,24小时后打印t2;
第二种输出:
t2 t1
即24小时后同时打印t2和t1。
这两种情况中,#4在v的修改顺序中都先于#1。合规的C++实现理论上是否允许这两种结果?也就是允许时间线上后执行的操作在原子对象的修改顺序中先于先执行的操作?
更新示例
#include <iostream> #include <atomic> #include <thread> #include <vector> int main() { std::atomic<int> v = 0; std::vector<std::thread> threads; for (int i = 0;i < 2;i++) { int index = 0; std::cin >> index; std::cout << "input " << index<<"\n"; std::thread t([&, index]() { const int condition = index == 1 ? 2 : 0; if (v.exchange(index, std::memory_order::acq_rel) == condition) { // #index std::cout << "t" << index << "\n"; } }); threads.push_back(std::move(t)); } for (auto&& t : threads) { t.join(); } }
回答
结论:合规的C++实现不允许这两种输出,也不允许物理时间上后执行的原子操作在修改顺序中先于先执行的操作。
核心原因是C++内存模型严格遵循因果性原则:一个操作不可能观察到在物理时间上晚于它完成的操作的修改结果。
针对初始示例具体分析:
- #1操作在24小时前就已执行完毕,此时#4操作尚未启动,不可能对原子变量
v产生任何修改。因此#1的exchange只能读取到v的初始值0,不会触发t1的打印逻辑。 - 24小时后#4操作执行时,
v已经被#1设置为1,因此#4的exchange读取到的值是1,不会触发t2的打印逻辑。
也就是说,初始示例的正确输出应该是无任何打印内容,题目中提到的两种输出都不可能出现。
对于更新后的示例:
- 若先输入1再输入2:t1线程先执行#1操作,将
v设为1;t2线程后执行#2操作,读取到1,不满足条件,无打印。 - 若先输入2再输入1:t2线程先执行#2操作,将
v设为2;t1线程后执行#1操作,读取到2,触发t1打印;同时#2操作读取到初始值0,触发t2打印,最终输出t2和t1(顺序取决于线程调度)。
本质上,原子对象的修改顺序必须符合因果逻辑,不能出现“后执行的操作影响先执行的操作结果”的倒置情况,这是C++标准对内存模型的基本约束之一。
内容的提问来源于stack exchange,提问作者xmh0511
相关产品推荐
相关产品推荐

