x86架构下Relaxed Atomics相关技术问题咨询
关于x86架构下Relaxed Atomics的问题解答
问题1:非原子变量的并发读写是否存在数据竞争,会不会出现半写入?
首先明确两个核心点:
- 硬件层面:x86架构中,对齐的8字节及以下类型的读写操作确实是原子的,因此不会出现“半写入”(比如读取到42的部分二进制位)的情况。
- C++标准层面:这段代码存在数据竞争,属于未定义行为。因为
shared_var是非原子变量,并发的读、写操作没有任何同步机制,编译器可对非原子变量的读写做激进优化——比如将reader线程中shared_var的读取缓存到寄存器,导致线程永远看不到writer写入的42,陷入无限循环;或出现其他不符合预期的行为。
简单总结:硬件上不会出现半写入,但代码违反C++标准,行为完全不可预测。
问题2:原子变量+relaxed顺序与问题1代码的差异
两者的核心差异体现在标准语义和编译器行为上:
- 消除数据竞争:
std::atomic<uint32_t>保证变量的读写是C++标准定义的原子操作,并发读写不再属于未定义行为,程序行为完全可预测。 - 限制编译器优化:编译器不会对原子变量的读写做缓存到寄存器这类激进优化,
reader线程每次读取shared_var都会从内存(或缓存)获取最新值,最终一定会看到writer写入的42,不会无限循环。 - 内存语义区别:
memory_order_relaxed只保证操作的原子性,不提供任何内存屏障或跨线程顺序保证,但在x86架构下,relaxed的load/store硬件行为和普通对齐读写一致;相比非原子变量,原子操作强制编译器遵守内存可见性规则。 - 代码细节差异:问题2中
std::cout << shared_var会调用std::atomic的operator<<,内部默认使用memory_order_seq_cst的load操作,而while循环里用的是memory_order_relaxed的load——不过这在x86上硬件行为差异不大,主要是语义层面的区别。
问题3:写入先于读取时,是否会被重排导致读取不到42?
不会,原因如下:
- 编译器层面:
writer线程中,shared_var.store(42, std::memory_order_relaxed)之后紧接着std::cout << shared_var,单线程内cout需要读取到刚写入的42,根据C++的as-if规则,编译器不会将store操作重排到cout之后。 - 硬件层面:x86的TSO(Total Store Order)内存模型禁止store-store操作重排,writer线程中的store(写
shared_var)和cout的写操作(写标准输出)是两个写操作,硬件会保证它们按程序顺序执行。 - 缓存一致性:当writer的store操作完成后,x86的MESI缓存协议会使其他CPU缓存中
shared_var的副本失效,后续reader线程的load操作会读取到最新的42值。 - acquire语义:reader线程用
memory_order_acquire的load,在x86上等价于普通load,但它保证后续操作不会重排到load之前,不过这里核心是写入已经先于读取执行,硬件的缓存一致性会保证读取到最新值。
综上,只要写入确实先于读取执行,读取一定能看到42,不会出现写入被重排到读取之后的情况。
内容的提问来源于stack exchange,提问作者Santiago
相关产品推荐
相关产品推荐

