详细介绍一下C++多线程同步之条件变量的典型用法
C 多线程同步条件变量std::condition_variable详细用法 完整示例这是 C11 原生多线程最常用、最高效的同步机制专门解决线程需要等待某个条件满足才能继续执行的场景。一、核心概念必须懂1. 作用让线程阻塞等待直到其他线程通知它条件已成立再唤醒继续运行。不浪费 CPU不会死循环空转精准控制线程执行顺序用于生产者 - 消费者、线程池、数据就绪通知等2. 三剑客固定搭配std::mutex互斥锁保护共享数据std::condition_variable条件变量等待 / 唤醒共享条件bool / 队列 / 计数判断是否要等待3. 核心 API表格函数作用wait(lock, predicate)阻塞等待自动解锁被唤醒后重新加锁并检查条件notify_one()唤醒一个正在等待的线程notify_all()唤醒所有正在等待的线程二、最重要规则必看wait必须搭配std::unique_lock不能用lock_guard必须带判断条件防止虚假唤醒修改共享条件时必须加锁唤醒可以在解锁后执行效率更高三、最经典用法生产者 - 消费者模型这是条件变量面试必考 工程最常用的写法。完整可运行代码cpp运行#include iostream #include thread #include mutex #include condition_variable #include queue using namespace std; // 共享队列 锁 条件变量 queueint g_queue; // 共享数据 mutex g_mtx; // 互斥锁 condition_variable g_cv; // 条件变量 bool g_stop false; // 停止标志 // 生产者往队列放数据 void producer() { for (int i 1; i 5; i) { this_thread::sleep_for(chrono::seconds(1)); // 模拟生产耗时 // 1. 加锁修改共享数据 unique_lockmutex lock(g_mtx); g_queue.push(i); cout [生产] i endl; // 2. 通知消费者有数据了 g_cv.notify_one(); } // 生产结束 lock_guardmutex lock(g_mtx); g_stop true; g_cv.notify_all(); // 唤醒所有等待线程 } // 消费者从队列取数据 void consumer() { while (true) { // 1. 加锁 unique_lockmutex lock(g_mtx); // 2. 等待条件队列不为空 或 停止生产 g_cv.wait(lock, [] { return !g_queue.empty() || g_stop; }); // 3. 被唤醒后判断是否退出 if (g_stop g_queue.empty()) { cout [消费] 队列空退出 endl; break; } // 4. 取数据 int val g_queue.front(); g_queue.pop(); cout [消费] val endl; } } int main() { thread t_pro(producer); thread t_con(consumer); t_pro.join(); t_con.join(); return 0; }运行效果plaintext[生产] 1 [消费] 1 [生产] 2 [消费] 2 [生产] 3 [消费] 3 [生产] 4 [消费] 4 [生产] 5 [消费] 5 [消费] 队列空退出四、wait到底做了什么底层逻辑cpp运行cv.wait(lock, []{ return 条件; });等价于cpp运行while (!条件) { cv.wait(lock); // 阻塞 自动解锁 } // 被唤醒后 → 自动重新加锁等待时释放锁让其他线程能修改数据被唤醒后自动加锁保证操作安全五、另一种经典用法线程执行顺序控制让线程 B必须等线程 A 完成后才能运行。cpp运行#include iostream #include thread #include mutex #include condition_variable using namespace std; mutex mtx; condition_variable cv; bool is_ready false; void threadA() { cout A执行任务... endl; this_thread::sleep_for(chrono::seconds(1)); lock_guardmutex lock(mtx); is_ready true; // 设置条件 cv.notify_one(); // 通知B cout A任务完成通知B endl; } void threadB() { unique_lockmutex lock(mtx); // 等待 is_ready true cv.wait(lock, []{ return is_ready; }); cout B收到通知开始执行 endl; } int main() { thread t1(threadA); thread t2(threadB); t1.join(); t2.join(); return 0; }六、高频面试点1. 什么是虚假唤醒操作系统可能无缘无故唤醒线程所以必须用 while / 判断条件重新检查。2. 为什么必须用 unique_lock因为wait内部需要自动解锁 重新加锁lock_guard不支持动态解锁。3. notify_one 和 notify_all 区别notify_one()唤醒一个等待线程效率高notify_all()唤醒全部用于广播事件七、总结极简记忆条件变量 等待 唤醒必须配合 mutex 共享条件使用wait用unique_lock 条件判断最常用场景生产者 - 消费者核心口诀先锁、再等、后通知