You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为std::condition_variable设置就绪标志的正确方式

std::condition_variable设置就绪标志的正确方式

兄弟,听我一句劝,你可千万别把signaler里那行std::lock_guard的注释留着!哪怕你用的是std::atomic<bool>类型的ready,也必须在设置它的时候锁住对应的mtx互斥量,我给你掰扯清楚这里面的门道:

1. 彻底躲开「丢失唤醒」的大坑

你想啊,要是不锁这个互斥量,搞不好就会碰到这种倒霉情况:worker线程刚拿到mtx,检查ready是false,正准备释放锁进入等待队列的那一瞬间,signaler线程直接把ready设为true还调用了notify_one。这时候worker还没真正挂到condition_variable的等待队列上,这个唤醒信号就等于白发了——worker之后会一直卡在wait里等一个永远不会来的信号,这就是圈内人常说的lost wakeup(丢失唤醒),踩过这个坑的人都懂有多闹心。

但你要是给signaler里设置ready的代码加上lock_guard,就能从根源上避免这种情况:要么signaler先拿到锁,把ready设为true再发唤醒信号,这时候worker之后调用wait时,一检查ready就是true,直接跳过等待;要么worker先拿到锁,进入wait后释放锁,signaler才能拿到锁设置ready并发信号,这时候worker肯定已经在等待队列里了,绝对不会错过唤醒。

2. 符合C++标准的同步规则

虽然std::atomic本身能保证内存可见性,但C++标准里对condition_variable的使用有明确要求:修改等待条件(也就是这里的ready)的操作,必须和wait调用处于同一个同步序列里——说白了就是得持有同一个互斥量。这样才能确保被唤醒的worker线程,能100%看到ready的最新值,不会因为CPU缓存或者指令重排搞出莫名其妙的内存可见性问题。

修正后的完整代码

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <atomic>
#include <chrono>

std::mutex mtx;
std::condition_variable cv;
std::atomic<bool> ready = false;

void worker() {
    std::unique_lock<std::mutex> lock(mtx);
    std::cout << "Waiting for signal...\n";
    cv.wait(lock, [] { return ready.load(); });
    std::cout << "Continue\n";
}

void signaler() {
    std::this_thread::sleep_for(std::chrono::seconds(2));
    {
        std::lock_guard<std::mutex> lock(mtx); // 这行必须加上!
        ready = true;
        std::cout << "Data ready\n";
    }
    cv.notify_one();
}

int main() {
    std::thread t1(worker);
    std::thread t2(signaler);
    t1.join();
    t2.join();
    return 0;
}

说白了,哪怕用了atomic变量,condition_variable配套的互斥量也得老老实实锁上——这不是多此一举,是从根源上避免各种诡异线程竞态问题的关键!

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 08:45:32