You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

线程间临界数据必须使用atomic类型而非普通内置类型吗?

普通bool替代atomic_bool的线程安全性问题

我正在阅读《CPP-Concurrency-In-Action-2ed-2019》一书,在5.3章节中作者给出如下示例,用来讲解如何使用std::atomic<bool>实现线程间的代码序列控制(同步):

#include <vector>
#include <atomic>
#include <iostream>

std::vector<int> data;
std::atomic<bool> data_ready(false);

void reader_thread()
{
  while(!data_ready.load())  // 1
  {
    std::this_thread::sleep(std::milliseconds(1));
  }
  std::cout<<"The answer="<<data[0]<<"\n";  // 2
}
void writer_thread()
{
  data.push_back(42);  // 3
  data_ready=true;  // 4
}

但我产生疑问:若用普通bool替代std::atomic<bool>,程序仍能保证线程安全吗?修改后的代码如下:

#include <vector>
#include <atomic>
#include <iostream>

std::vector<int> data;
bool data_ready(false);

void reader_thread()
{
  while(!data_ready)  // 1
  {
    std::this_thread::sleep(std::milliseconds(1));
  }
  std::cout<<"The answer="<<data[0]<<"\n";  // 2
}
void writer_thread()
{
  data.push_back(42);  // 3
  data_ready=true;  // 4
}

我知晓+=、-=这类复合操作并非线程安全,但仅对POD变量进行简单读写(如data_ready=true)本身是否就不安全?查阅相关问答后仍未能理解其中要点。


问题解答

普通bool无法保证线程安全,核心原因有两点:

  • 缺乏内存可见性保障
    writer线程修改普通bool变量data_ready后,reader线程可能永远无法看到这个修改。编译器会因为优化,将data_ready的值缓存到寄存器中,reader线程的循环会一直读取寄存器里的旧值,不会去同步内存中的新值;另外CPU缓存一致性机制也可能没有及时同步不同核心之间的缓存,导致reader核心看不到writer核心写入的新值。

  • 没有原子操作与happens-before关系的保障
    原示例中的std::atomic<bool>操作会建立严格的happens-before关系:writer线程中data.push_back(42)(代码3)一定先于data_ready=true(代码4)执行,reader线程中data_ready的读取(代码1)一定先于data[0]的读取(代码2)执行,同时代码4的修改一定先于代码1的读取被观测到,最终保证reader线程读到的是已经初始化完成的data。
    换成普通bool后,这个同步关系完全失效:编译器可能重排指令,把data_ready=true提前到data.push_back(42)之前执行;即使指令没重排,reader线程读到data_ready=true后,也可能因为缓存未同步,看到的data还是未初始化的状态,读取data[0]会触发未定义行为。

关于单字节POD读写的误区

很多人误以为单字节POD变量的读写是原子的,但C++标准仅保证原子类型的操作具备原子性,普通变量哪怕是单字节,也没有这个保证——不同CPU架构、编译器优化都可能打破原子性。而且就算原子性侥幸成立,内存可见性的问题依然存在,这才是导致同步失效的关键。


内容的提问来源于stack exchange,提问作者f1msch

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 14:52:45