C++20内存模型释放序列定义变更对示例程序的影响分析
C11与C20内存模型释放序列变更对示例程序的影响
首先看示例代码:
// 初始状态 std::atomic<int> x{0}; int y{0}; // 线程1 y = 1; // A x.store(1, std::memory_order_release); // B x.store(3, std::memory_order_relaxed); // C // 线程2 if (x.load(std::memory_order_acquire) == 3) // D print(y); // E
C++11中的行为
在C++11的内存模型中,释放序列允许同一线程后续的普通写入操作加入前一个释放操作的序列。这里操作C(relaxed语义的store)属于B(release语义的store)引领的释放序列,因此当线程2的D操作(acquire语义的load)读取到C写入的3时,D会与B建立同步关系。
由于A happens-before B,B同步于D,D happens-before E,根据happens-before的传递性,A happens-before E。这就保证了E执行时y必然是1,所以只要程序有输出,结果一定是1,不存在数据竞争。
C++20中的行为变更
C++20对释放序列的定义做了关键修改:同一线程执行的非读改写(RMW)写入操作,不再被纳入前一个释放操作的释放序列。
回到示例中,C是普通的relaxed store,不属于B的释放序列。当D读取到3时,只能关联到C的写入,但C是relaxed操作,无法与D建立同步关系。此时A和E之间不存在任何happens-before关系——虽然所有线程对x的修改顺序是一致的(先B写1,再C写3),但这种修改顺序的一致性无法传递到非原子变量y的可见性上。
这就导致程序存在数据竞争:非原子变量y的写入操作A和读取操作E分属不同线程,且两者之间没有happens-before关系。因此E执行时可能看到y的初始值0,也可能看到A写入的1;同时如果D没读取到3,程序就不会有输出。
总结来说,C++20的变更让该程序可能输出0或1,且存在数据竞争。
内容的提问来源于stack exchange,提问作者Alastair Harrison
相关产品推荐
相关产品推荐

