原子线程栅栏:非原子变量isDataReady为何存在数据竞争?是否有影响?
为什么非原子的
isDataReady会存在数据竞争? 咱们先从C++标准的核心定义入手拆解这个问题,这是理解一切的关键:
C++标准明确规定:当两个不同线程对同一个内存位置执行非原子操作,且至少有一个操作是写入时,就构成了数据竞争,这属于严重的未定义行为(UB)。
回到你的代码场景
你的代码里,isDataReady是普通的bool类型(非原子):
- 生产者线程执行
isDataReady = true;(非原子写入操作) - 消费者线程执行
while(!isDataReady);(非原子读取操作)
这两个操作分别在不同线程中对同一内存位置进行读写,完全符合数据竞争的定义。哪怕你觉得“实际运行好像没出问题”,这也是标准明确禁止的未定义行为——编译器和CPU可不会按你“直觉”的逻辑来处理。
为什么这真的会造成问题?
你提到“是不是因为写入可能在读取前未完成?”,其实更严重的是编译器/CPU的优化会彻底打乱你的预期:
- 编译器看到消费者线程里的
while(!isDataReady),由于isDataReady是非原子变量,它会默认认为这个变量不会被其他线程修改,于是直接把isDataReady的值缓存到CPU寄存器里,不再重新读取内存。结果就是消费者线程会进入永久死循环,永远看不到生产者写入的true。 - 就算编译器没做这个优化,CPU的乱序执行也可能让读取结果完全不可预测——非原子操作没有任何内存顺序保证,你无法确定读取到的
isDataReady是生产者写入前还是写入后的值。
正确的做法是什么?
把isDataReady改成std::atomic<bool>类型,哪怕用memory_order_relaxed也没问题:
std::atomic<bool> isDataReady = false; int data = 0; void Producer() { data = 42; std::atomic_thread_fence(std::memory_order_release); isDataReady.store(true, std::memory_order_relaxed); } void Consumer() { while(!isDataReady.load(std::memory_order_relaxed)); std::atomic_thread_fence(std::memory_order_acquire); assert(data == 42); }
原子操作本身就避免了数据竞争,而你原有的release/acquire栅栏已经保证了data的写入会在isDataReady的写入之前被消费者可见,所以用relaxed顺序完全足够,不会影响内存可见性的保证。
内容的提问来源于stack exchange,提问作者Antoine Morrier
相关产品推荐
相关产品推荐

