c++oding="utf-8" ?>
std::atomic::wait仅对支持等待机制的原子类型有效,需平台支持(如futex)且满足has_wait约束;调用前须static_assert检查,否则可能静默失败或忙等。

std::atomic::wait 为什么不能直接用在所有原子类型上
std::atomic::wait 不是万能的,它只对 std::atomic<bool></bool>、std::atomic<:uint32_t></:uint32_t> 等部分整型特化(以及 std::atomic<:uintptr_t></:uintptr_t> 等)可用,且要求平台支持等待/通知机制(如 Linux 的 futex、Windows 的 WaitOnAddress)。
如果你对 std::atomic<int></int> 调用 wait,编译可能通过(取决于标准库实现),但运行时大概率静默失败或退化为忙等——因为底层没注册等待队列。
- 必须确认你的原子类型是「可等待的」:查 cppreference 上
std::atomic<t>::wait</t>的约束条件,重点看is_always_lock_free和has_wait静态成员 - 常见陷阱:用
std::atomic<int></int>在 macOS 上调用wait,实际不生效(Apple libc++ 目前不支持) - 推荐显式检查:
static_assert(std::atomic<int>::has_wait, "wait not supported");</int>
如何正确触发 std::atomic::notify_one / notify_all
notify_one 和 notify_all 不是“发信号”就完事,它们必须和 wait 配对使用在同一内存位置,且通知必须发生在等待线程已进入等待状态之后(否则丢失)。
- 典型错误:先
store新值,再notify_one,但等待线程还没来得及调用wait,通知就飞走了 → 永久阻塞 - 正确做法:用循环 + 条件检查确保“值已变且通知有效”,例如:
while (flag.load() == false) { flag.wait(false); // 等待变为 true } // 此时 flag 已为 true,无需再 load -
notify_one只唤醒一个等待线程;notify_all唤醒全部——但唤醒后仍要重新检查条件(spurious wakeup),所以 wait 循环必不可少
wait 的参数为什么必须是“当前期望值”而不是目标值
wait 的签名是 void wait(T oldval, std::memory_order order = std::memory_order_seq_cst) const noexcept;,这里的 oldval 是你认为当前值应该是多少,不是你想等到的值。
- 如果你写
counter.wait(42),意思是:“等 counter 变成别的值(即不再是 42)”,而不是“等它变成 42” - 所以常见模式是:先
load当前值,再传给wait,比如:auto expected = ready.load(); if (!expected) { ready.wait(expected); // 等 ready 改变(即变成 true) } - 错误写法:
ready.wait(true)—— 这是在等ready从true变成别的值,逻辑反了
性能对比:wait/notify vs 自旋 or condition_variable
std::atomic::wait 的优势在于无锁、零系统调用开销(futex 仅在真正阻塞时陷入内核),但前提是等待时间足够长(微秒级以上),否则不如自旋。
- 短等待(while(!flag.load()) { } 更快,避免上下文切换成本
- 中长等待(1us–1ms):
wait显著优于std::condition_variable(后者需 mutex + syscall + 调度器介入) - 注意:
wait不提供超时接口(C++20 标准没加),需要超时就得回退到condition_variable或手动轮询 +std::this_thread::yield()
真正难的是让通知和等待在时间上对齐,以及跨平台兼容性判断——这两点比写几行代码更耗调试时间。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











