C++ 多线程:条件变量防虚假唤醒与 atomic 急停信号
一、背景
机器人控制回路里,线程同步主要就两类问题:
- 等事件:等传感器数据、等规划结果、等上位机指令。
- 发信号:急停、看门狗超时、安全区域越界。
条件变量解决前者,atomic 解决后者。下面按 SOP 整理。
二、SOP 实操
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 保存为 exercise7.cpp |
文件名与代码一致 |
| 2 | g++ -std=c++17 -pthread exercise7.cpp -o exercise7 |
编译 |
| 3 | ./exercise7 |
运行 |
| 4 | 观察输出 | 先跑生产者-消费者,再跑急停 |
| 5 | 验证结果 | 消费者收到 42;运动被急停打断 |
三、架构
程序分两块:
- 条件变量同步 :producer 写数据并置
ready = true,consumer 通过cv.wait带谓词等待,防止虚假唤醒。 - atomic 跨线程信号 :main 线程写
emergencyStop,safetyMonitor 和 motionController 只读,实现无锁急停。
四、重难点解决方案
| 问题 | 原因 | 解法 |
|---|---|---|
| 虚假唤醒 | 线程可能未 notify 就被唤醒 | cv.wait 必须带谓词,或用 while 循环 |
| if 判断 ready 不可靠 | 唤醒后不再检查,队列可能为空 | 改成 cv.wait(lk, []{ return ready; }) |
| notify 放在锁内 | 被唤醒线程可能立刻阻塞在锁上 | notify_one 放在锁外 |
| 急停信号跨线程不可见 | 普通 bool 不保证可见性 | 用 std::atomic<bool> |
| 急停用 relaxed 是否安全 | relaxed 只保证原子性 | 单一标志位、无其他变量顺序依赖时可用 |
| 多变量有顺序依赖 | relaxed 不保证先后顺序 | 升级为 acquire-release,或回到 mutex |
五、预期输出
=== 条件变量生产者-消费者 ===
生产者:已发送数据并通知
消费者:收到数据 42
=== atomic急停信号 ===
[运动控制] 执行第 1 步
[运动控制] 执行第 2 步
[运动控制] 收到急停,停止运动
[安全监控] 检测到急停信号!立即切断电机!
步数取决于调度顺序,但急停一定会触发。
六、工程要点
- 条件变量:wait 永远带谓词,别用 if;notify 尽量放锁外。
- atomic 急停:单一标志位用 relaxed 性能最优;多变量有顺序依赖时升级为 acquire-release。
- 机器人场景:急停按钮、看门狗、安全越界都用 atomic 广播,比 mutex 轻,不阻塞控制回路。