能用 std::atomic::wait 的类型必须满足 is_always_lock_free == true 且为 std::atomic、std::atomic、std::atomic、std::atomic、std::atomic、std::atomic、std::atomic、std::atomic、std::atomic、std::atomic、std::atomic、std::atomic、std::atomic、std::atomic 或对应指针类型;常见错误是误用 std::atomic(在 ARM32 等平台可能非 lock-free),应显式选用 guaranteed lock-free 类型如 std::atomic 或 std::atomic,并配合循环检查与内存序同步。

std::atomic::wait 和 std::atomic::notify_* 在 C++20 中才可用,且仅对部分整型和指针类型的 std::atomic 实例有效;不支持自定义类型、浮点型或 std::atomic(除非底层是整型实现且编译器允许)。
哪些类型能用 std::atomic::wait?
必须满足 is_always_lock_free == true 且是下列之一:std::atomic<:uint8_t>、std::atomic<:uint16_t>、std::atomic<:uint32_t>、std::atomic<:uint64_t>(含对应有符号型)、std::atomic<:uintptr_t>、std::atomic。常见错误是直接对 std::atomic 调用 wait —— 在某些平台(如 ARM32 或旧 x86)上,int 可能不是 always lock-free,导致编译失败或运行时抛出 std::system_error(error_code = std::errc::operation_not_supported)。
验证方式:
static_assert(std::atomic<int>{}.is_always_lock_free, "int atomic not always lock-free on this platform");</int>
更稳妥的做法是显式选用已知支持的类型,比如:
-
std::atomic<:uint32_t>(x86/x64/ARM64 均 guaranteed lock-free) -
std::atomic<:uintptr_t>(适合指针状态标记) - 避免
std::atomic:虽然语义简洁,但标准未要求其实现支持 wait/notify,GCC/Clang 当前通常拒绝调用
wait 的语义不是“等待任意变化”,而是“等待值不等于预期”
调用 val.wait(expected, timeout) 时,线程会休眠直到:val.load() 返回值 ≠ expected。它不是监听“变更事件”,而是基于当前观测值做条件等待。这意味着:
- 如果调用前
val已经 ≠expected,wait立即返回(无休眠) - 若其他线程在你调用
wait前就完成了notify_one,这次通知会丢失(无唤醒队列,不累积) - 典型误用:用
flag.wait(false)等待 flag 变为 true —— 若 flag 已为 true,直接跳过;若中间发生 notify 但没被 catch,就会死等
正确模式是配合循环检查:
while (flag.load() == false) { flag.wait(false); }
注意:wait 内部会自动重读值,但外部仍需手动 reload 判断,防止虚假唤醒(spurious wakeup)。
notify_one 和 notify_all 不保证立即唤醒,也不保证唤醒顺序
notify_one 从等待队列中选一个线程唤醒,notify_all 唤醒全部——但这两者都不阻塞,也不同步内存。关键点:
- notify 发生时,若无线程正在 wait,通知直接丢弃(无队列缓冲)
- notify 不隐含 memory_order:它默认按
memory_order_seq_cst同步,但若你用store更新原子变量时用了memory_order_relaxed,而 notify 前没加 fence,可能造成观察到的值与通知不一致 - 推荐配对写法:
state.store(READY, std::memory_order_release);<br>state.notify_all(); // 自动带 release 语义(C++20 规定)
不过,不同编译器对 notify 的内存序处理略有差异,保守起见可在 notify 前加 std::atomic_thread_fence(std::memory_order_release),尤其在混合使用 relaxed store 的场景。
实际用作事件通知时,必须处理超时与中断逻辑
wait 支持带 std::chrono::duration 的重载,但返回值是 void —— 它不会告诉你是否超时。真正判断超时,得靠 wait_until:
auto tp = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);<br>if (flag.wait_until(false, tp) == std::cv_status::timeout) { /* 处理超时 */ }
但注意:wait_until 返回 std::cv_status::timeout 仅表示“等到时间点仍未满足条件”,不代表期间没被唤醒过(虚假唤醒仍可能发生)。所以仍要配合循环检查:
auto tp = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);<br>while (flag.load() == false) {<br> if (flag.wait_until(false, tp) == std::cv_status::timeout) break;<br>}
另外,Linux 上底层依赖 futex,Windows 依赖 WaitOnAddress;这些系统调用可被信号中断(Linux)或 APC 打断(Windows),导致 wait 提前返回 —— 这也是为什么必须循环检查,不能只信一次返回。
最易被忽略的一点:wait/notify 是 per-object 的,不是 per-value 的。同一个原子变量上多次不同 expected 值的 wait 调用,共享同一等待队列。这意味着 notify 可能唤醒“等错值”的线程,进一步强化了循环检查的必要性。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











