std::atomic<bool>与bool的区别

1. 根本区别

特性std::atomic<bool>普通 bool
原子性✅ 保证读/写是原子操作❌ 不保证原子性
内存顺序✅ 可控制内存访问顺序❌ 无保证
线程安全✅ 多线程读写安全❌ 需要额外同步
编译器优化屏障✅ 阻止不安全的指令重排❌ 无屏障作用

2. 为什么普通 bool 不够?

(1) 撕裂问题 (Tearing)
  • 在32+位系统上,bool 可能被编译器优化为位域操作
  • 非原子操作可能导致部分写入(如同时读写时看到半旧半新的值)
(2) 内存可见性问题
// 线程A
should_exit = true;  // 普通bool写入可能缓存在CPU核心本地缓存

// 线程B
while(!should_exit) { /*...*/ }  // 可能永远看不到线程A的修改
(3) 指令重排风险
// 编译器/CPU可能重排指令
init_resources();
should_exit = false;  // 普通bool可能被重排到init_resources()之前

3. std::atomic<bool> 的保障

(1) 硬件级原子性
  • x86: 通过 LOCK 前缀指令保证总线锁定
  • ARM: 使用 LDREX/STREX 指令实现原子操作
(2) 内存顺序控制
std::atomic<bool> flag{false};

// 线程A
flag.store(true, std::memory_order_release);

// 线程B
while(!flag.load(std::memory_order_acquire)) { /*...*/ }
(3) 编译器屏障

阻止跨原子变量的指令重排:

// 保证顺序性
a = 1;
flag.store(true);  // 编译器不会将这两行调换顺序
b = 2;

4. 性能对比

操作x86-64 (ns/op)
普通 bool~0.3
atomic<bool>~5-20
带内存屏障的原子写~20-100

5. 何时可以用普通 bool?

仅在以下场景可安全使用:

  1. 单线程环境
  2. 多线程只读不写
  3. 已有其他同步机制(如互斥锁保护)

6. 最佳实践示例

(1) 退出标志
std::atomic<bool> should_exit{false};

// 控制线程
void ctrl_thread() {
    std::cin.get();  // 等待输入
    should_exit.store(true);
}

// 工作线程
void worker_thread() {
    while(!should_exit.load()) {
        // 处理任务
    }
}
(2) 双重检查锁定
std::atomic<bool> initialized{false};
std::mutex mtx;

void lazy_init() {
    if(!initialized.load(std::memory_order_acquire)) {
        std::lock_guard<std::mutex> lock(mtx);
        if(!initialized) {
            init_resources();
            initialized.store(true, std::memory_order_release);
        }
    }
}

7. 各平台实现差异

平台原子性实现方式
x86-64MOV 指令直接支持字节级原子操作
ARMv8需要显式使用 LDAXR/STLXR 指令
嵌入式MCU可能禁用缓存,依赖总线锁

总结:std::atomic<bool> 是线程安全的,而普通 bool 不是。在涉及多线程共享的标志变量时,必须使用原子类型以避免未定义行为。虽然原子操作有性能开销,但这是保证正确性的必要代价。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值