c++oding="utf-8" ?>
std::atomic::wait不能替代condition_variable,因其仅支持精确值匹配等待、无谓词机制、不绑定互斥锁,且notify丢失通知无缓冲;必须配合while循环重检状态、正确内存序及平台支持。

std::atomic::wait 为什么不能直接替代 condition_variable
因为 std::atomic::wait 只能等待一个原子变量的**精确值变化**,它不支持谓词等待、不维护等待队列上下文、也不和互斥锁绑定。如果你需要“等待某个条件成立(比如 flag.load() == true)”,直接写 var.wait(expected) 是危险的——expected 可能早已被其他线程改写,导致虚假唤醒或永久阻塞。
典型错误场景:
- 用
flag.wait(false)等待布尔标志变为true,但flag在调用wait前已被设为true,于是线程永远等不到通知 - 多个线程同时
wait同一个值,但notify_one只唤醒一个,其余线程没重检条件就继续休眠,逻辑卡死
正确使用 wait/notify 的三要素:load + 循环 + compare_exchange
标准做法是把 wait 放在 while 循环里,每次唤醒后重新读取当前值,并用 compare_exchange_weak 检查是否满足退出条件。这不是可选优化,而是语义必需。
示例(等待 ready 变为 true):
std::atomic<bool> ready{false};
// 等待方
while (!ready.load(std::memory_order_acquire)) {
ready.wait(false, std::memory_order_relaxed);
}
// 此时 ready 一定为 true,且 acquire 栅栏生效
</bool>
关键点:
-
wait的第一个参数必须是**调用时读到的旧值**,不是目标值;否则可能跳过已发生的变更 -
load和wait之间必须无竞态——所以要用循环,每次wait返回都重新load -
notify_one/notify_all必须在修改原子变量**之后**调用,且修改本身需有足够内存序(如store(true, std::memory_order_release))
notify 调用时机不对会导致 wait 永远不返回
std::atomic::notify_one 和 notify_all 不是“发信号”,而是“唤醒当前正在 wait 的线程”。如果 notify 发生在任何线程调用 wait 之前,这个通知就丢失了——没有线程在等,通知就没了。
这和 pthread_cond_signal 或 condition_variable::notify_one 的行为一致,但更容易被忽略,因为 atomic 的 notify 看起来像“广播”。
安全模式只有两种:
- 先确保至少一个线程已进入
wait(比如用另一个原子变量做入场标记) - 用“乐观尝试+回退”:先
load判断条件是否已满足,满足则跳过wait;不满足再wait,然后notify方严格在store后调用notify
漏掉 notify 或顺序颠倒(notify 在 store 前),都会让等待线程卡死。
wait/notify 不跨平台:Windows 和旧版 libc++ 不支持
std::atomic::wait 是 C++20 引入的特性,依赖底层 futex(Linux)、WaitOnAddress(Windows)或 os_unfair_lock(macOS)。GCC 11+、Clang 12+、MSVC 19.29+ 才完整支持。
常见兼容性陷阱:
- macOS 上 Clang 13 以前版本对
std::atomic<int>::wait</int>会静默退化为忙等待(spin loop),CPU 占用飙升 - 交叉编译时未开启
-std=c++20或未定义__cpp_lib_atomic_wait宏,编译失败 - 某些嵌入式 STL(如 newlib)完全不提供 wait/notify 实现,编译报错
no member named 'wait'
上线前务必在目标环境验证 __cpp_lib_atomic_wait 是否定义,且实测唤醒延迟和 CPU 占用。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











