当前位置: 首页 > news >正文

详细讲解条件变量

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>class ThreadPrinter {
private:int count;// 共享资源:当前数字int max;// 共享资源:最大数字bool turn; // true表示第一个线程打印,false表示第二个线程打印std::mutex mtx;// 保护以上三个共享资源的锁std::condition_variable cv;// 用于线程间通信的条件变量public:ThreadPrinter(int max) : count(1), max(max), turn(true) {}void print(int threadId) {while (count <= max) {// 获取锁 - 关键步骤!std::unique_lock<std::mutex> lock(mtx);// 从这里开始,当前线程独占了共享资源:其他线程无法访问这些变量 count max turn// 检查是否轮到当前线程while ((threadId == 1 && !turn) || (threadId == 2 && turn)) {//条件不满足,进入等待状态cv.wait(lock);/*
释放锁 mtx(让其他线程可以访问共享资源)将线程置于等待状态(不消耗CPU)将线程加入到条件变量的等待队列中当其他线程调用 cv.notify_all() 时:所有等待的线程被标记为可运行状态线程尝试重新获取锁获取到锁后,从 wait() 调用中返回*/}// 执行打印工作(此时持有锁)std::cout << "Thread " << threadId << ": " << count << std::endl;count++;// 切换控制权turn = !turn;// 通知另一个线程可以打印了 通知其他线程cv.notify_all();//锁在作用域结束时自动释放// lock 析构函数被调用 → mtx.unlock()}}
};int main() {ThreadPrinter printer(100);std::thread t1(&ThreadPrinter::print, &printer, 1);std::thread t2(&ThreadPrinter::print, &printer, 2);t1.join();t2.join();return 0;
}

1. 条件变量是什么?

条件变量是一种线程同步机制,允许线程在某个条件不满足时等待,当条件满足时被其他线程唤醒

核心方法

  • wait():让线程等待

  • notify_one():唤醒一个等待的线程

  • notify_all():唤醒所有等待的线程

2. cv.wait(lock) 的详细工作原理

cpp

std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock);

wait() 内部执行三个原子操作

  1. 释放锁:让其他线程可以访问共享资源

  2. 进入等待状态:线程挂起,不消耗CPU

  3. 被唤醒后重新获取锁:继续执行后续代码

相当于

cpp

// wait() 的伪代码实现
void condition_variable::wait(std::unique_lock<std::mutex>& lock) {// 1. 释放锁,让其他线程运行lock.unlock();// 2. 将当前线程加入等待队列,进入睡眠状态add_to_waiting_queue(current_thread);sleep_current_thread();// 3. 被唤醒后,重新获取锁lock.lock();
}

3. 为什么用 while 而不用 if

这是条件变量使用中最关键的理解点!

使用 if 的问题:

cpp

// 错误的方式!
if ((threadId == 1 && !turn) || (threadId == 2 && turn)) {cv.wait(lock);
}
// 被唤醒后直接继续执行,不再检查条件

可能发生的问题

  1. 虚假唤醒:线程可能在没有收到 notify 的情况下被唤醒

  2. 条件变化:在等待期间,条件可能被其他线程改变

  3. 多个线程同时通过:多个等待的线程可能同时被唤醒

使用 while 的正确性:

cpp

// 正确的方式!
while ((threadId == 1 && !turn) || (threadId == 2 && turn)) {cv.wait(lock);
}
// 被唤醒后重新检查条件,确保真的轮到我了

while 循环的保护作用

  • 每次被唤醒都重新检查条件

  • 如果条件仍不满足,继续等待

  • 确保只有条件真正满足时才继续执行

4. 在这段代码中的具体工作流程

让我们通过具体的执行序列来理解:

初始状态:

cpp

count = 1, turn = true, max = 100
执行序列1:正常情况

text

时间点: t0     t1     t2     t3     t4
线程1:  获取锁 → 检查条件 → 打印1 → 设置turn=false → 通知
线程2:        等待锁 → 检查条件 → 等待 → 被唤醒 → 打印2

详细步骤

  1. 线程1:获取锁,检查条件 (1==1 && !true) = false → 不等待,直接打印

  2. 线程1:设置 turn = false,调用 cv.notify_all(),释放锁

  3. 线程2:获取锁,检查条件 (2==2 && false) = false → 不等待,直接打印

  4. 线程2:设置 turn = true,调用 cv.notify_all(),释放锁

执行序列2:线程竞争情况

text

时间点: t0     t1     t2     t3     t4     t5
线程1:  获取锁 → 检查条件 → 打印1 → 设置turn=false → 通知 → 释放锁
线程2:        等待锁 → 获取锁 → 检查条件 → 等待 → 被唤醒 → 打印2

5. 虚假唤醒的实际例子

假设由于系统调度或硬件原因,线程2在没有收到通知的情况下被唤醒:

cpp

// 线程2的执行流程:
1. 获取锁
2. 检查条件: (2==2 && true) = true → 进入等待
3. 虚假唤醒!(没有收到notify,但被系统唤醒了)
4. 重新检查条件: (2==2 && true) = true → 继续等待 ✓
5. 真正被线程1唤醒
6. 重新检查条件: (2==2 && false) = false → 开始打印 ✓

如果用 if,在第3步就会错误地开始执行打印操作!

6. notify_all() 的作用

cpp

cv.notify_all();  // 唤醒所有等待的线程

为什么要用 notify_all() 而不是 notify_one()

在这个例子中,虽然只有一个线程在等待,但使用 notify_all() 更安全:

  • 确保所有等待的线程都被唤醒

  • 避免某个线程永远等待的情况

  • 代码更健壮,易于扩展

7. 完整的条件变量使用模式

标准的使用模板:

cpp

// 等待线程
{std::unique_lock<std::mutex> lock(mtx);while (!condition_is_satisfied) {  // 必须用whilecv.wait(lock);}// 执行需要同步的操作
}// 通知线程  
{std::lock_guard<std::mutex> lock(mtx);// 修改条件condition_is_satisfied = true;cv.notify_all();  // 或 cv.notify_one()
}

8. 总结

为什么用 while 不用 if

  1. 防止虚假唤醒:系统可能无故唤醒线程

  2. 条件重检查:确保被唤醒时条件真正满足

  3. 多线程安全:多个线程可能同时被唤醒

条件变量的核心思想

  • 等待时释放锁:让其他线程可以工作

  • 被唤醒时重新获取锁:保证数据一致性

  • 循环检查条件:确保条件真正满足

http://www.dtcms.com/a/525458.html

相关文章:

  • 筑牢风控生命线:金仓数据库替代MongoDB,重构证券融资融券业务的数据基石
  • 网站的时间对齐应该怎么做ftp跟网络连接Wordpress
  • 阿里云国际站GPU:怎么通过控制台自助排查功能诊断GPU?
  • 做商贸网站安徽省住房建设部官方网站
  • 华为ENSP——NAT实验
  • 上海手机响应式网站建设设计血液中心网站建设规范
  • 基于transform的scale属性,实现数据可视化大屏自适应缩放适配不同分辨率
  • 正规的咨询行业网站策划263企业邮箱报价
  • 杭州画廊网站建设嘉兴网站制作维护
  • 深度可分离卷积
  • TikTok 独立 IP 解决方案:独享静态住宅 IP + 环境隔离 + 粘性会话
  • App自动化测试详解
  • SQL-Server2019离线部署安装【CentOS7.4】
  • ps做 网站标准尺寸是多少合适沈阳哪个网站建设公司好
  • Vue 3 核心技术演示平台 - 完整技术文档
  • 建设网站的HTML代码百度网盘电脑版
  • 力扣49位置字母异位词
  • 企业网站优化推广公司江西哪家网站建设公司好
  • 网站建设销售求职东营网格通下载安装包
  • 权限管理域——RBAC模型权限系统设计
  • Makefile中的函数
  • 在 KubeSphere 上构建你的自托管 AI 助手|Open WebUI 扩展上线
  • 以太网的性能参数
  • LazyLLM,(万象应用开发平台 AppStudio)商汤大装置
  • 合肥智能建站模板余杭区网站建设设计公司
  • CentOS7 部署主从复制MariaDB数据库
  • ArcGIS产品构成
  • 基于Go语言开发的GIS空间分析库Gogeo使用教程
  • 10.24快乐!
  • 成都商报官方网站做酒业网站的要求