You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

std::memory_order_relaxed为何允许副作用重排序?

核心误解

你搞错了最基础的规则边界:sequenced-before只约束单线程内的可观测行为,根本不管跨线程的内存可见顺序,这是刚接触C++内存模型的人最容易踩的坑。


先搞懂sequenced-before到底约束什么

你引用的所有标准条文里提到的“前序语句的所有副作用完成后才执行后续语句”,从来没有要求代码里的内存操作必须严格按书写顺序物理执行,更不要求操作对其他线程的可见顺序和代码顺序一致。

这里的“副作用完成”有非常明确的适用边界:

  • 只对当前执行线程负责:后续代码只要能观测到前序操作的效果就算满足要求,比如你写了变量a,后面读a一定能拿到你写的值,规则就成立。
  • 完全不要求操作效果全局可见:CPU可以把值存在私有存储缓冲区、编译器可以把值放在寄存器里,哪怕几百个周期之后才同步到共享缓存、其他线程才能看到,只要当前线程自己读值不出错,所有优化都合法。
  • 只要单线程视角下的执行结果和代码严格按顺序跑的结果完全一致(也就是符合as-if规则),编译器、CPU可以任意调整指令实际执行顺序、调整内存操作对外的可见顺序。

举个最通俗的例子:你先后写两个完全不相关的普通变量a=1;b=2;,编译器完全可以把b=2的汇编指令放到a=1前面,只要你自己线程后面读a是1、读b是2,就一点不违反标准。


回到示例,重排完全不违反单线程规则

我们拆解线程2的两个操作:

  • C:r2 = x.load(std::memory_order_relaxed):读共享变量x,把结果写入线程私有栈变量r2
  • D:y.store(42, std::memory_order_relaxed):写共享变量y为42

这两个操作没有任何数据依赖:C读的是x、写的是r2,D写的是y,三个变量完全不重叠。不管你先执行C还是先执行D,线程2自己观测到的结果不会有任何区别:

  • 按代码顺序先C后D:r2拿到x的值,再写y=42,后续线程2读r2是x的加载值、读y是42
  • 把D挪到C前面先执行:先写y=42,再读x把值写到r2,后续线程2读r2还是x的加载值、读y还是42

两种执行顺序对线程2自己来说完全等价,根本不存在“违反sequenced-before”的问题。你觉得矛盾,本质是自行给sequenced-before加了一条标准从来没提过的规则:“前序操作必须比后序操作先对其他线程可见”。

你提到的strongly happens-before规则也有适用前提:跨线程的strongly happens-before关系必须通过同步操作(比如锁、acquire-release操作对、seq_cst原子操作的全局顺序)才能建立。同一个线程内的两个relaxed原子操作就算有sequenced-before关系,这个顺序也不会传递给其他线程,其他线程完全可以观测到两个操作的顺序反过来。


r1 == r2 == 42的合法执行时序

这个结果的出现根本不需要打破任何单线程顺序保证,一种完全符合标准的执行流如下:

  1. 线程2先执行D操作:把y=42的写请求发送到缓存系统,这个写操作很快通过缓存一致性协议传播到了线程1的本地缓存
  2. 线程1执行A操作:读y,刚好拿到已经传播过来的42,写入自己的私有变量r1
  3. 线程1执行B操作:把r1的值42写入x,这个写请求也很快传播到了线程2的本地缓存
  4. 线程2执行C操作:读x,刚好拿到线程1写入的42,写入自己的私有变量r2

整个过程两个线程的单线程逻辑完全自洽:

  • 线程1严格按A→B顺序执行,先读y拿42写r1,再写x=42,自己观测不到任何异常
  • 线程2虽然先把y的写请求发出去了,但它在执行C之前从来没有读过y,根本不会观测到“D在C前面执行”的痕迹;执行C的时候也确实正确拿到了x的值写入r2,后续用r2的时候也不会拿到错误值,完全符合单线程语义要求。

补充:为什么线程1的A和B不能重排

这俩操作有明确的数据依赖:B写入x的值直接来自A加载y得到的r1。如果把B挪到A前面,r1还没被赋值,写x的值就完全不对了,会直接改变单线程的执行结果,违反as-if规则,所以不管用什么内存序,这种有明确依赖的操作都不会被重排。


内容的提问来源于stack exchange,提问作者senor-tomate

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 14:27:18