关于__ATOMIC_RELAXED结合if语句的指令重排问题咨询
问题分析与解答
代码示例
int flag = 0; int threadA() { if(__atomic_load_n(&flag, __ATOMIC_RELAXED)) { return -1; } // do something A return 0; } int threadB() { __atomic_store_n(&flag, 1, __ATOMIC_RELEASE); // do something B __atomic_store_n(&flag, 0, __ATOMIC_RELEASE); return 0; }
需求说明
当threadB()执行// do something B时,不希望threadA()执行相关操作。
核心疑问
由于__ATOMIC_RELAXED不阻止指令重排,threadA()中的// do something A是否可能在if语句之前执行?
解答
1. 直接结论:不会发生这种重排
即使使用__ATOMIC_RELAXED内存序,threadA里的// do something A也不可能在if(__atomic_load_n(...))判断之前执行。
2. 原因解释
指令重排的规则不会破坏单线程内的程序顺序一致性——也就是站在当前线程的视角,代码的执行逻辑必须符合你写的顺序。
- 对于
threadA来说,// do something A在if分支的外部,只有当if条件不成立(flag为0)时才会执行。编译器和CPU无论怎么重排指令,都不能让这段代码跑到if判断之前,否则会彻底违背单线程的逻辑语义。 __ATOMIC_RELAXED的“不阻止重排”,指的是不同线程之间的操作可见性和顺序性约束,而非单线程内部的逻辑顺序。它不会允许编译器/CPU把后续代码提前到分支判断之前执行。
3. 当前代码的线程安全隐患
虽然你担心的重排不会发生,但这段代码的内存序搭配无法满足核心需求:threadB执行do something B时,threadA仍可能进入执行do something A。
原因是threadA用__ATOMIC_RELAXED加载flag,threadB用__ATOMIC_RELEASE存储flag,两者之间没有同步关系。threadB设置的flag=1可能无法及时被threadA看到,导致threadB已在执行do something B,threadA还读到flag=0并进入执行。
4. 修复方案
要满足需求,需要给threadA的原子加载加上__ATOMIC_ACQUIRE内存序,和threadB的__ATOMIC_RELEASE形成ACQUIRE-RELEASE同步:
int threadA() { // 用ACQUIRE加载,和RELEASE存储建立同步 if(__atomic_load_n(&flag, __ATOMIC_ACQUIRE)) { return -1; } // do something A return 0; }
这样:
threadB的__ATOMIC_RELEASE存储会保证// do something B之前的所有操作(包括设置flag=1)对后续用__ATOMIC_ACQUIRE加载flag的线程可见;threadA的__ATOMIC_ACQUIRE加载会保证,只有当它看到flag=0时,后续的// do something A才会执行,且能看到threadB之前的所有已完成操作。
内容的提问来源于stack exchange,提问作者Sujin Lee
相关产品推荐
相关产品推荐

