std::atomic::wait 更高效因为其让线程休眠而不占用 cpu,而 while 循环是忙等待;它依赖 futex/waitonaddress 机制,仅在变量被 notify 时唤醒,要求类型 lock-free、c++20 及以上且需 load-check-wait-check 三步调用。

std::atomic::wait 为什么比 while 循环更高效
因为 std::atomic::wait 让线程真正进入休眠状态,不消耗 CPU;而 while (x.load() != expected) 是忙等待,尤其在多核系统上会持续抢占调度时间片,还可能触发虚假唤醒或缓存行频繁无效化。
它依赖底层 futex(Linux)或 WaitOnAddress(Windows),只有当变量实际被 notify_one/notify_all 修改时才被唤醒。前提是变量必须是 lock-free 的 std::atomic 类型(如 int、bool、指针),且对齐满足要求(通常 alignas(std::atomic<int>)</int> 不用显式写,但自定义结构体要小心)。
- 仅适用于 C++20 及以上,且标准库需支持 —— GCC 11+、Clang 12+、MSVC 19.30+ 才完整实现
- 不能用于
std::atomic<:string></:string>或任何非平凡类型(编译直接报错:error: atomic wait not supported for type) - 等待前必须确保当前值等于预期值,否则立即返回(不阻塞)
正确调用 std::atomic::wait 的三步写法
不是直接传值比较,而是先读取当前值,再以该值为条件等待 —— 这能避免竞态和误唤醒后漏处理。
std::atomic<int> flag{0};
// 等待 flag 变成 1
int expected = flag.load();
while (expected != 1) {
std::atomic_wait(&flag, expected); // 传入地址和当前值
expected = flag.load(); // 重新读取,检查是否真变了
}</int>
- 必须用
flag.load()初始化expected,不能写死std::atomic_wait(&flag, 1)—— 因为如果 flag 已经是 1,调用会直接返回,但你没做后续处理 -
std::atomic_wait是无锁等待,但内部可能需要短暂加锁来注册等待者,所以仍建议在临界区外调用 - 唤醒后务必再次
load()确认值,不能假设“醒了就一定是目标值”(可能被虚假唤醒)
std::atomic::notify_one 和 notify_all 的使用边界
notify_one 只唤醒一个等待者,适合“信号量”或“单生产者-单消费者”场景;notify_all 唤醒全部,适合广播通知(比如配置重载),但要注意:唤醒数量多时可能引发惊群效应。
- 必须在修改原子变量**之后**调用 notify —— 否则等待线程可能错过通知(即使变量已改,但 wait 还没注册进等待队列)
- notify 操作本身不保证内存顺序,如需同步其他数据,应搭配
memory_order_release写 +memory_order_acquire读 - 没有“notify_if_changed”这种 API,自己得判断值是否真变了再 notify,否则空唤醒浪费资源
常见崩溃和静默失败场景
最典型的是对未初始化的 std::atomic 调用 wait —— 比如全局变量未显式初始化,其值是未定义的,std::atomic_wait 可能传入非法地址或陷入内核态错误。
- 在 shared memory 中跨进程使用?不行。
std::atomic::wait仅限同一进程内线程间通信,不支持进程间同步 - 用
std::atomic<int64_t></int64_t>在 32 位平台?GCC 可能 fallback 到锁实现,wait将不可用(std::atomic_is_lock_free返回 false) - 忘记链接 pthread(Linux)或未开启 /await(MSVC)?编译不报错,但运行时
std::atomic_wait直接 abort
真正用起来,核心就三点:C++20 环境、lock-free 基础类型、load-check-wait-check 的循环模式。其它都是边界问题。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











