关于C11内存栅栏(memory fence)与原子操作的技术疑问
内存屏障相关疑问解答
先看两段对比代码:
//version 1 Thread A: *val = 1; atomic_thread_fence(memory_order_release); atomic_store_explicit(published, 1, memory_order_relaxed); Thread B: if (atomic_load_explicit(published, memory_order_relaxed) == 1) { atomic_thread_fence(memory_order_acquire); assert(*val == 1); // will never fail } //version 2 /* Thread A */ *val = 1; atomic_thread_fence(memory_order_release); *published = 1; /* Thread B */ if (*published == 1) { atomic_thread_fence(memory_order_acquire); assert(*val == 1); /* may fail */ }
问题1:atomic_thread_fence是否仅影响原子加载/存储操作?它对编译器和CPU是否都有作用?
- 不是,
atomic_thread_fence的作用范围不局限于原子操作:- 对编译器:它会阻止编译器对屏障前后的所有内存操作(无论原子还是非原子)进行重排序,确保屏障前的写操作都被安排在屏障执行前,屏障后的操作在屏障执行后。
- 对CPU:它会生成硬件级别的内存屏障指令,阻止CPU对屏障前后的内存操作进行乱序执行,同样覆盖所有类型的内存操作。
本质上它是全局的内存操作约束,而非仅针对原子API。
问题2:在版本2中,对published的存储是非原子操作,为何使用atomic_thread_fence仍会导致断言失败?
首先纠正误解:atomic_thread_fence并非“仅针对原子操作”,但版本2断言失败的核心原因是:
- 线程A对
published的非原子写,无法和线程B对published的非原子读形成synchronize-with(同步关系)。 - 版本1中,原子存储
atomic_store_explicit和原子加载atomic_load_explicit配合release/acquire屏障,能建立跨线程同步:线程A屏障前的*val=1会被线程B在屏障后可靠可见。 - 而版本2里,非原子的读写操作本身不保证可见性,CPU可能缓存
val的值未同步到主存,或编译器对非原子操作做了优化导致值未及时更新,即使加了屏障也无法弥补非原子操作缺失的同步语义。
问题3:为何*val = 1未写成atomic_store_explicit(val, 1, memory_order_relaxed)?
val的操作不需要原子语义:在版本1的同步机制下(release/acquire屏障+原子published操作),线程B只有在确认published被原子设置后才会读取val,此时线程A对val的写操作已完成,且通过屏障保证了可见性。- 使用普通非原子写更高效:原子操作会带来额外的CPU同步开销,在不需要原子语义的场景下,普通内存操作完全满足需求且性能更好。
内容的提问来源于stack exchange,提问作者ehow
相关产品推荐
相关产品推荐

