C++11中静态bool是否线程安全?单写多读无同步场景是否合法
问题结论
这段代码在C++11标准下属于未定义行为,static属性完全没有规避多线程数据竞争的作用。
为什么static不解决问题
static修饰的类成员变量本质只是将变量的存储周期改为静态存储、作用域限定在类作用域,和多线程下的内存可见性、同步规则没有任何关联,没有任何标准条款会为static变量赋予额外的多线程同步语义。
C++11对数据竞争的明确规定
C++11标准明确判定数据竞争的规则:当多个线程同时访问同一个内存位置,且其中至少有一个是写操作、所有访问之间没有happens-before关系时,就构成数据竞争,属于未定义行为。
你的场景完全符合数据竞争的判定条件:
- 写线程执行
A::f = true是写操作 - 读线程循环读取
A::f是读操作 - 两者之间没有任何同步操作(无互斥锁、无原子变量、无内存屏障),不存在happens-before关系,因此必然触发未定义行为。
两个常见误区的解释
误区1:bool操作是原子的就没有问题
即使你所用的CPU上bool的读写是单指令原子操作,也解决不了内存可见性和指令重排问题:
- 编译器可能会把读线程里
A::isF()的返回值优化为寄存器缓存,循环过程中永远不会重新读取内存中的f的值,导致循环永远无法退出 - 不做同步的情况下,写线程修改的
f可能一直留在写核心的存储缓冲区里,没有刷回共享内存,读线程所在的核心永远看不到更新后的值
误区2:static变量有特殊的多线程语义
只有函数内部的static局部变量的初始化在C++11之后被规定为线程安全,除此之外static变量没有任何多线程相关的特殊规则。你的场景是static变量初始化完成之后的读写操作,和这个规则完全无关。
正确的修改方案
最简单的兼容单写多读场景的修改方式是把f的类型改为std::atomic<bool>,不需要额外加锁就能保证访问安全:
#include <atomic> struct A { static std::atomic<bool> f; static bool isF() { return f.load(std::memory_order_acquire); } }; //Source A std::atomic<bool> A::f = false; void threadWriter(){ /* Do something */ A::f.store(true, std::memory_order_release); } // Source B void threadReader(){ while (!A::isF()) { /* Do something */} }
如果不需要极致性能,也可以直接用std::atomic<bool>的默认读写操作,默认内存序为顺序一致性,逻辑完全正确,仅性能有微小损耗。
内容的提问来源于stack exchange,提问作者Baptiste Wicht
相关产品推荐
相关产品推荐

