如何不使用谓词解决条件变量spurious wakeup提前触发问题
解决方案
完全不需要更换实现方案,也不需要使用wait_for的谓词重载,只需要新增共享标记变量区分唤醒类型,同时增加实际耗时校验即可解决虚假唤醒问题,具体实现如下:
第一步:定义线程共享变量
所有变量都需要和条件变量绑定的同一把互斥锁保护:
#include <mutex> #include <condition_variable> #include <chrono> std::mutex mtx; std::condition_variable cv; std::chrono::seconds current_interval; // 你的动态修改的等待时长x bool interval_updated = false; // 标记是否为t2修改x触发的主动唤醒
第二步:t1线程执行逻辑
void t1_routine() { std::unique_lock<std::mutex> lock(mtx); while (true) { // 处理x为0的休眠场景 if (current_interval.count() == 0) { cv.wait(lock); // 无限等待t2唤醒 interval_updated = false; continue; } // 记录本次等待的起始时间点(必须用稳定时钟避免系统时间干扰) const auto wait_start = std::chrono::steady_clock::now(); const auto wait_res = cv.wait_for(lock, current_interval); // 情况1:等待返回状态为超时 if (wait_res == std::cv_status::timeout) { // 额外校验实际耗时,排除虚假唤醒被误判为超时的情况 const auto elapsed = std::chrono::duration_cast<std::chrono::seconds>( std::chrono::steady_clock::now() - wait_start ); if (elapsed >= current_interval) { // 真超时,先解锁再执行发送,避免阻塞t2修改x lock.unlock(); /* 这里执行你的发送逻辑 */ lock.lock(); interval_updated = false; } // 未到等待时长,属于虚假唤醒,回到循环继续等待剩余时间 continue; } // 情况2:等待返回状态为非超时 // 是t2修改x触发的主动唤醒,重置标记后用新x重新计时 if (interval_updated) { interval_updated = false; continue; } // 其余情况都是虚假唤醒,回到循环继续等待剩余时间 } }
第三步:t2线程修改x的逻辑
void update_interval(std::chrono::seconds new_x) { std::lock_guard<std::mutex> lock(mtx); current_interval = new_x; interval_updated = true; cv.notify_one(); // 主动唤醒t1 }
实现说明
- 核心思路是通过
interval_updated标记区分主动唤醒和虚假唤醒,同时通过稳定时钟的实际耗时校验,确保只有真的等够x秒才会执行发送操作 - x为0的场景完全符合需求:t1进入无限等待,t2只要调用
update_interval就可以无条件唤醒t1 - 不需要额外更换实现方案,原生条件变量完全可以满足你的需求,只要注意所有共享变量的读写都必须加互斥锁即可
内容的提问来源于stack exchange,提问作者VK13
相关产品推荐
相关产品推荐

