单线程内宽松原子内存访问的顺序保证及C++标准相关问题
关于C++原子操作线程内顺序与内存屏障等效性的问题解答
场景1:线程内原子store与load的顺序保证
问题代码
void foo() { // 此处A != 42 A.store(42, std::memory_order::relaxed); auto a = A.load(std::memory_order::relaxed); if (a != 42) assert("What!?") }
其中A为全局std::atomic<int>变量,其他线程仅对其进行读取。问题是:当某线程调用foo()时,C++的哪条语言规则保证该线程内A.store(42, ...)在sequenced before关系上先于A.load(...)?
解答
C++标准通过线程内的程序顺序(program order)与sequenced before关系保证这一点:
- 根据[intro.execution]条款,同一线程中,按程序书写顺序出现的完整表达式,前者sequenced before后者。具体来说,
A.store(42, std::memory_order::relaxed)作为完整表达式,sequenced before后续的A.load(...)完整表达式。 - 对于原子操作,[atomics.order]条款明确:同一线程中,对同一原子对象的原子操作,若前者sequenced before后者,则后者必然能看到前者操作的结果——即使使用
memory_order::relaxed内存顺序。
这意味着,调用foo()的线程中,store操作的修改一定会被后续的load读取到,断言永远不会触发。
场景2:seq_cst store与seq_cst栅栏的等效性
问题代码
修改后的foo():
void foo() { // 此处A != 42 A.store(42, std::memory_order::relaxed); B.store(0, std::memory_order::seq_cst); auto a = A.load(std::memory_order::relaxed); if (a != 42) assert("What!?") }
其中新增全局std::atomic<int>变量B。在x64平台上,这段代码(除修改B外)等效于:
void foo() { // 此处A != 42 A.store(42, std::memory_order::relaxed); std::atomic_thread_fence(std::memory_order::seq_cst); auto a = A.load(std::memory_order::relaxed); if (a != 42) assert("What!?") }
问题是:从C++语言标准角度,是否存在这样的等效保证?
解答
从C++标准角度,不存在这样的等效保证,原因如下:
- 语义本质不同:
std::atomic_thread_fence(std::memory_order::seq_cst)是独立内存屏障,作用是同步线程间的所有原子操作:栅栏前的所有原子写对其他线程的seq_cst读可见,栅栏后的所有原子读能看到其他线程之前的seq_cst写。B.store(0, std::memory_order::seq_cst)是针对B的seq_cst级原子写,仅参与全局seq_cst总顺序,同步语义围绕B展开,而非像栅栏一样对所有原子操作进行全局同步。
- 线程内顺序的依赖逻辑不同:
虽然同一线程内A.storesequenced beforeB.store,后者又sequenced beforeA.load,保证了线程内读取结果正确,但这依赖的是线程内的sequenced before关系,而非seq_cst store的栅栏效果。标准并未规定seq_cst store可以替代seq_cst栅栏的全局同步语义——x64平台的类似表现只是特定实现细节,而非标准强制要求。
内容的提问来源于stack exchange,提问作者Alexander Spichak
相关产品推荐
相关产品推荐

