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

C++ std::condition_variable使用示例及共享变量加锁必要性疑问

std::condition_variable 使用示例及常见问题解答

基础使用示例

以下是std::condition_variable的一个简单使用示例:

#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
#include <chrono>             // 原示例隐式依赖的头文件

std::mutex mtx;
std::condition_variable cv;
int global_status = 0;

void print_id(int id) 
{
    std::unique_lock<std::mutex> lck(mtx);
    while (global_status == 0)
    {
        cv.wait(lck);
    }
    std::cout << "thread " << id << '\n';
}

int main()
{
    std::thread threads[10];
    for (int i = 0; i < 10; ++i)
    {
        threads[i] = std::thread(print_id, i);
    }
  
    std::cout << "Start" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(5));            

    {
        std::unique_lock<std::mutex> lck(mtx);
        global_status = 1;
        cv.notify_all();
    }

    for (auto& th : threads) th.join();
    return 0;
}

为什么修改 global_status 时必须加锁?

哪怕只有单个写线程,加锁依然是强制必要的,核心原因有三个:

  • 第一是避免数据竞争的未定义行为。global_status是普通的非原子变量,写线程修改它的同时,可能有读线程(所有执行print_id的工作线程)正在读取它的值,没有锁保护的跨线程读写非原子变量,属于C++标准明确规定的「数据竞争」,会直接触发未定义行为,程序运行结果完全不可控。
  • 第二是保证内存可见性。现代CPU都有多层缓存,没有锁或者原子操作的内存屏障语义的话,写线程对global_status的修改可能只会存在于它自己的核心缓存里,其他工作线程永远看不到这个修改,就算你发了notify,工作线程醒过来读的还是旧的0值,会再次进入wait状态永远卡住。
  • 第三是避免通知丢失的时序问题。如果不加锁,你可能先调用notify_all,之后才修改global_status的值,刚好有工作线程在这两个操作的间隙醒过来(比如之前的虚假唤醒刚结束重新判断条件),这时候读到的global_status还是0,回去继续wait,之后你再修改global_status已经不会再发通知了,这些线程就永远卡着了。

哪怕你把global_status改成std::atomic<int>,依然不能省略这把锁,上述第三种时序问题依然存在,notify和变量修改的顺序没有锁保护的话还是可能乱序,最终导致通知丢失。只要是和condition_variable配合使用的状态变量,不管是不是原子类型,读写都必须在同一个mutex的保护下进行,这是C++标准里对condition_variable使用的强制要求。

内容的提问来源于stack exchange,提问作者Optimus1

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 01:45:04