C++ 条件变量虚假唤醒问题的解决

在 C++ 中,std::condition_variablewaitwait_for 方法除了可以传入一个锁(std::unique_lock),还可以传入一个谓词函数 (函数或可调用对象)。这个谓词的作用是让条件变量在特定的条件满足时才退出等待。


1. 谓词的作用

谓词是一个返回布尔值的函数或可调用对象,它用于判断某个条件是否满足。如果条件不满足,wait 会继续阻塞当前线程;如果条件满足,线程会立即退出等待状态。

为什么需要谓词?
  • 避免虚假唤醒(Spurious Wakeups)。虚假唤醒是指线程被唤醒后,实际并没有满足预期的条件。
  • 简化代码逻辑。通过传入谓词,避免手动使用循环反复检查条件。

2. 常见用法示例

无谓词的使用

如果不使用谓词,必须手动在循环中检查条件:

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void worker() {
    std::unique_lock<std::mutex> lock(mtx);
    // 使用循环检查条件
    while (!ready) {
        cv.wait(lock);  // 等待唤醒
    }
    std::cout << "Worker is proceeding!" << std::endl;
}

void notifier() {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::unique_lock<std::mutex> lock(mtx);
    ready = true;
    cv.notify_one();  // 唤醒一个等待线程
}

int main() {
    std::thread t1(worker);
    std::thread t2(notifier);

    t1.join();
    t2.join();
    return 0;
}

在这种情况下,cv.wait(lock); 只是等待被唤醒,但必须手动在循环中检查 ready 是否为 true。否则,线程可能会因为虚假唤醒提前退出。


带谓词的使用

通过传入谓词,可以让 wait 方法自动检查条件,避免手动循环:

cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void worker() {
    std::unique_lock<std::mutex> lock(mtx);
    // 使用谓词,只有条件满足时才退出等待
    cv.wait(lock, [] { return ready; });
    std::cout << "Worker is proceeding!" << std::endl;
}

void notifier() {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::unique_lock<std::mutex> lock(mtx);
    ready = true;
    cv.notify_one();  // 唤醒一个等待线程
}

int main() {
    std::thread t1(worker);
    std::thread t2(notifier);

    t1.join();
    t2.join();
    return 0;
}

解释:

  • cv.wait(lock, [] { return ready; }); 表示:只有当 readytrue 时,wait 才会退出等待。
  • 如果 readyfalsewait 会重新进入阻塞状态,直到被唤醒并再次检查条件(谓词)。

3. 谓词的优点

(1) 避免虚假唤醒

虚假唤醒可能会导致线程在条件未满足的情况下提前退出等待状态。如果不使用谓词,必须通过循环反复检查条件。

(2) 简化代码逻辑

使用谓词将条件检查逻辑封装到 wait 方法中,代码更简洁、更清晰。


4. 常见的 wait 方法

(1) wait
cpp 复制代码
cv.wait(lock);                     // 等待被唤醒
cv.wait(lock, [] { return ready; }); // 等待被唤醒,同时检查谓词
(2) wait_for

wait_for 用于等待一定的时间,并同时检查谓词:

cpp 复制代码
if (cv.wait_for(lock, std::chrono::seconds(2), [] { return ready; })) {
    std::cout << "Condition met within 2 seconds!" << std::endl;
} else {
    std::cout << "Timeout!" << std::endl;
}
(3) wait_until

wait_until 用于等待到某个时间点,并同时检查谓词:

cpp 复制代码
auto timeout = std::chrono::system_clock::now() + std::chrono::seconds(2);
if (cv.wait_until(lock, timeout, [] { return ready; })) {
    std::cout << "Condition met before timeout!" << std::endl;
} else {
    std::cout << "Timeout!" << std::endl;
}

5. 总结

  • 谓词的作用:自动检查条件是否满足,避免虚假唤醒,简化等待逻辑。
  • 常见写法cv.wait(lock, [] { return condition; });,只有当 conditiontrue 时,线程才会退出等待。
  • 使用场景:在多线程程序中,配合条件变量和锁,用于线程间的同步与通信。
相关推荐
遇见你的雩风6 分钟前
LeetCode 118 题解--杨辉三角
java·算法·leetcode·职场和发展
WW、forever9 分钟前
【ConvLSTM第二期】模拟视频帧的时序建模(Python代码实现)
开发语言·python·音视频
远瞻。10 分钟前
【论文精读】2024 CVPR--Upscale-A-Video现实世界视频超分辨率(RealWorld VSR)
论文阅读·人工智能·算法·stable diffusion·音视频·超分辨率重建
Daking-26 分钟前
「动态规划::状压DP」网格图递推 / AcWing 292|327(C++)
c++·算法·动态规划
hjjdebug31 分钟前
c/c++怎样编写可变参数函数.
c++·args...·可变参数函数
web1508541593532 分钟前
用 Python 模拟雪花飘落效果
开发语言·python·pygame
mozun202033 分钟前
YOLOX 的动态标签分类(如 SimOTA)与 Anchor-free 机制解析2025.5.29
人工智能·算法·yolo·目标检测·计算机视觉·分类·数据挖掘
码上飞扬33 分钟前
JavaScript性能优化实战
开发语言·javascript·性能优化
未来之窗软件服务34 分钟前
JavaScript性能优化实战的技术文-——仙盟创梦IDE
开发语言·javascript·ecmascript·代码优化·仙盟创梦ide
阿卡蒂奥1 小时前
PaddleOCR本地部署 (Python+Flask)
开发语言·python·flask