std::atomic 比 mutex + condition_variable 更轻量,因其底层通常编译为单条 CPU 指令,无系统调用和内核态切换;而 condition_variable 需线程挂起/唤醒,开销高一个数量级。
实现线程间的轻量级通知">
因为 std::atomic<bool></bool> 底层通常编译为单条 CPU 指令(如 x86 的 lock xchg 或 mov + 内存屏障),不涉及系统调用、内核态切换或等待队列管理。而 std::condition_variable 必须配合 std::mutex 使用,每次通知/等待都可能触发线程挂起/唤醒,开销高一个数量级。
但它只适合「单次通知」或「状态轮询」场景——不能像条件变量那样携带 payload、无法阻塞等待「直到某条件成立」,也不能广播或重置通知状态(除非手动干预)。
std::atomic<bool></bool> 的 ABA 和忙等陷阱?常见错误是写成 while 循环持续读取:while (!flag.load()) { /* 空转 */ },这会吃满 CPU 核心,且在低优先级线程中可能延迟响应。
std::this_thread::yield() 让出时间片:while (!flag.load(std::memory_order_acquire)) { std::this_thread::yield(); }
std::this_thread::sleep_for() 配合指数退避(仅适用于对延迟不敏感的场景)std::atomic<bool></bool> 本身不涉及 ABA 问题(只有 true/false 两个值),但若你用它模拟多状态(比如用 true 表示“就绪”,false 表示“未就绪”再加额外状态字段),就要注意状态耦合——此时应改用 std::atomic<int></int> 或 std::atomic_flag
典型模式是:生产者写 flag.store(true, std::memory_order_release),消费者读 flag.load(std::memory_order_acquire)。关键在内存序配对——release 写保证其前所有内存操作对 acquire 读可见。
立即学习“C++免费学习笔记(深入)”;
但要注意:一旦消费者读到 true,flag 就处于“已通知”状态;如果消费者没做清理,后续循环还会立即进入。所以单次通知必须配合手动重置:
std::atomic<bool> ready{false};<p>// 生产者data = 42; // 准备数据ready.store(true, std::memory_order_release); // 发送通知</p><p>// 消费者while (!ready.load(std::memory_order_acquire)) {std::this_thread::yield();}// 此时 data 已就绪use(data);ready.store(false, std::memory_order_relaxed); // 重置,供下次使用</p>
重置用 relaxed 即可——因为此时没有其他线程依赖这个写操作的同步语义。
std::atomic_flag 是 C++ 中唯一保证无锁(lock-free)的原子类型,且默认初始化为 clear(即 false),语义更贴近「通知旗标」。而 std::atomic<bool></bool> 在某些平台(如旧 ARM)可能被实现为带锁的封装,导致意外性能回退。
如果你只需要「设旗 → 查旗 → 清旗」三步,且不关心初始值是否可设为 true,优先用 std::atomic_flag:
std::atomic_flag ready = ATOMIC_FLAG_INIT;<p>// 通知(原子 set)ready.test_and_set(std::memory_order_release);</p><p>// 等待(自旋)while (ready.test(std::memory_order_acquire)) {std::this_thread::yield();}</p><p>// 注意:atomic_flag 没有直接的 clear 方法,需用 test_and_set(false)ready.clear(std::memory_order_relaxed);</p>
真正容易被忽略的是:不同平台对 std::atomic<bool></bool> 是否 lock-free 没有强制要求,运行时可用 flag.is_lock_free() 或 bool.is_lock_free() 检查——别只看文档,实测才是关键。