bigywater.tech
Rust · 91

Condvar 条件变量

条件变量总让人困惑:为什么 wait 要传 guard?为什么要用 while?通知会丢吗?notify_one 和 notify_all 差在哪?这篇每个问题都用一个真实运行的小实验回答。

1 对 5
notify_one 与 notify_all 在 5 个等待线程中各唤醒的线程数
通知丢失
先 notify 后 wait,没有状态变量时的结局
约 1 ms
阻塞队列跑了约 550 ms 墙钟时间所用的进程 CPU 时间

标准用法

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    等到条件为真,等了约 100 ms(取整到 50)

Mutex 的基础见Mutex 与 RwLock。

wait 释放锁

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    子线程正在 wait 时,主线程 try_lock 成功: true(wait 释放了锁)
    子线程醒来读到 7

通知会丢

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    先 notify 后 wait(没有状态变量):wait_timeout 超时了吗? true(通知丢失)
    先通知(置 true)后 wait_while:超时了吗? false  条件 = true

虚假唤醒(spurious wakeup)是 while 循环的另一个理由,这次实验没有复现出来。

notify_one 与 notify_all

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    5 个线程在 wait:notify_one 唤醒了 1 个;notify_all 唤醒了 5 个

阻塞队列

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    收到 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],墙钟时间约 550 ms,进程 CPU 时间约 1 ms(消费者睡眠,没有忙等)

通道封装了这个模式,见Channel 消息传递。

分步演示

Condvar
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。进程 CPU 时间通过 clock_gettime(CLOCK_PROCESS_CPUTIME_ID) 读取
    • 没有验证:虚假唤醒(没有复现)、Condvar 配多个 Mutex 的 panic、wait_timeout 的精度、notify 的顺序与公平性、parking_lot::Condvar
    京ICP备2026067502号-1京公网安备11010502063487号