条件锁存在的意义:用生活中的例子秒懂线程间的"暗号系统"
条件锁存在的意义:用生活中的例子秒懂线程间的"暗号系统"
引子:
在学习linux下c语言中的互斥锁和条件锁的时候,我的大脑哦逻辑进入了“条件锁到底锁了什么”的死循环,所以让deepseek给我举了三个生动的例子,感觉非常有帮助,记录在这里,这段回答仅用于理解条件锁的含义,代码准确性有待验证:
正文:
一句话核心:条件锁(条件变量)就像线程间的"暗号",让线程在特定条件下自动休眠或唤醒,避免无意义的循环检查,极大节省CPU资源。下面用5个生活场景带你彻底理解!
一、快递柜场景:包裹到了才取货(生产者-消费者模型)
█ 场景还原
- 快递员(生产者线程):每天不定时放包裹到快递柜
- 用户(消费者线程):想取包裹,但不想每隔5分钟就跑下楼检查
█ 不用条件锁的问题
用户每隔5分钟就跑去快递柜输入取件码,发现没包裹又回家——CPU疯狂空转(忙等待),既费腿(CPU资源)又低效。
█ 使用条件锁的优化
// 全局条件变量(快递柜通知系统)
pthread_cond_t has_package = PTHREAD_COND_INITIALIZER;
// 用户取件线程
void* user_thread(void* arg) {{
pthread_mutex_lock(&lock);
while(快递柜为空) {{
// 进入休眠,直到收到快递员的通知
pthread_cond_wait(&has_package, &lock);
}}
取出包裹();
pthread_mutex_unlock(&lock);
}}
// 快递员线程
void* courier_thread(void* arg) {{
pthread_mutex_lock(&lock);
放入包裹();
// 投递完成,触发通知(类似快递柜发短信)
pthread_cond_signal(&has_package);
pthread_mutex_unlock(&lock);
}}
效果:用户回家睡觉,收到短信才下楼取件——CPU利用率从99%降到5%。
二、餐厅等位场景:有空桌才能吃饭
█ 场景还原
- 顾客(线程A):到餐厅发现满座
- 服务员(线程B):需要等顾客吃完才能安排新座位
█ 传统互斥锁的尴尬
顾客每隔1分钟就问服务员:"有空位了吗?" 服务员不断回答:"还没!" ——双方都累。
█ 条件锁的优雅方案
pthread_cond_t table_available = PTHREAD_COND_INITIALIZER;
// 顾客线程
void* customer(void* arg) {{
pthread_mutex_lock(&restaurant_lock);
while(餐桌数 == 0) {{
// 安静等待服务员叫号
pthread_cond_wait(&table_available, &restaurant_lock);
}}
入座就餐();
pthread_mutex_unlock(&restaurant_lock);
}}
// 服务员线程
void* waiter(void* arg) {{
pthread_mutex_lock(&restaurant_lock);
清理餐桌();
空桌数++;
// 广播通知所有等待顾客(类似叫号屏刷新)
pthread_cond_broadcast(&table_available);
pthread_mutex_unlock(&restaurant_lock);
}}
优势:顾客喝茶休息,服务员只在有空桌时统一通知,沟通效率提升10倍。
三、流水线场景:组装必须按顺序
█ 场景还原
- 工序A(线程1):生产手机外壳
- 工序B(线程2):安装屏幕,但必须等外壳完成后才能开工
█ 无协调的灾难
工序B的工人不断跑到工序A车间检查进度——大量时间浪费在跑路上(线程切换开销)。
█ 条件锁的工业级方案
pthread_cond_t case_ready = PTHREAD_COND_INITIALIZER;
// 工序B线程
void* install_screen(void* arg) {{
pthread_mutex_lock(&factory_lock);
while(外壳完成数 == 0) {{
// 产线工人去休息室等待
pthread_cond_wait(&case_ready, &factory_lock);
}}
安装屏幕();
pthread_mutex_unlock(&factory_lock);
}}
// 工序A线程
void* make_case(void* arg) {{
pthread_mutex_lock(&factory_lock);
生产外壳();
外壳完成数++;
// 铃铛一响,通知所有安装工
pthread_cond_signal(&case_ready);
pthread_mutex_unlock(&factory_lock);
}}
结果:工序B工人只在铃响时开工,整体生产效率提升40%。
四、为什么不用互斥锁+循环检查?
对比实验:CPU占用率实测
方案 | CPU占用率 | 响应延迟 |
---|---|---|
纯互斥锁+usleep(1) | 25% | 1ms |
条件变量 | 0.3% | 0.1ms |
// 错误示范:忙等待消耗CPU
while(条件不满足) {{
pthread_mutex_unlock(&lock);
usleep(1000); // 让出CPU但仍有频繁切换
pthread_mutex_lock(&lock);
}}
结论:条件变量通过内核级休眠,比手动休眠减少 99%的线程唤醒次数。
五、条件锁的核心价值总结
- 消灭忙等待:让线程在条件不满足时主动让出CPU,而不是"占着茅坑不拉屎"
- 精准唤醒:通过
pthread_cond_signal
或pthread_cond_broadcast
控制唤醒策略 - 避免竞态条件:
pthread_cond_wait
会自动释放锁+进入等待队列,保证唤醒后能重新获得锁 - 跨线程协作:像交通信号灯一样协调多个线程的执行顺序
下次看到pthread_cond_wait
,就想象成线程在说:"这事我干不了,先睡会儿,有情况叫我!" ——这才是高效程序员的智慧。
条件锁存在的意义:用生活中的例子秒懂线程间的"暗号系统"的更多相关文章
- ThreadLocal实现session中用户信息 的线程间共享(精)
ThreadLocal并不难理解,我总结的最简单的理解就是: ThreadLocal像其它变量一样(局部.全局.静态)也是一种变量类型,只是他是线程变量,更直白的说他是一种变量作用域,即他的作用域是当 ...
- ThreadLocal实现session中用户信息 的线程间共享
转载自:http://blog.sina.com.cn/s/blog_4b5bc01101013gok.html ThreadLocal并不难理解,我总结的最简单的理解就是: ThreadLocal像 ...
- JAVA中管道通讯(线程间通讯)例子
Java I/O系统是建立在数据流概念之上的,而在UNIX/Linux中有一个类似的概念,就是管道,它具有将一个程序的输出当作另一个程序的输入的能力.在Java中,可以使用管道流进行线程之间的通信,输 ...
- 通过一个生活中的案例场景,揭开并发包底层AQS的神秘面纱
本文导读 生活中案例场景介绍 联想到 AQS 到底是什么 AQS 的设计初衷 揭秘 AQS 底层实现 最后的总结 当你在学习某一个技能的时候,是否曾有过这样的感觉,就是同一个技能点学完了之后,过了一段 ...
- 死磕 java同步系列之ReentrantLock源码解析(二)——条件锁
问题 (1)条件锁是什么? (2)条件锁适用于什么场景? (3)条件锁的await()是在其它线程signal()的时候唤醒的吗? 简介 条件锁,是指在获取锁之后发现当前业务场景自己无法处理,而需要等 ...
- JS规则 我或你都可以 (逻辑或操作符)||逻辑或操作符,相当于生活中的“或者”,当两个条件中有任一个条件满足,“逻辑或”的运算结果就为“真”
我或你都可以 (逻辑或操作符) "||"逻辑或操作符,相当于生活中的"或者",当两个条件中有任一个条件满足,"逻辑或"的运算结果就为&quo ...
- InnoDB的锁机制浅析(二)—探索InnoDB中的锁(Record锁/Gap锁/Next-key锁/插入意向锁)
Record锁/Gap锁/Next-key锁/插入意向锁 文章总共分为五个部分: InnoDB的锁机制浅析(一)-基本概念/兼容矩阵 InnoDB的锁机制浅析(二)-探索InnoDB中的锁(Recor ...
- linux c 线程间同步(通信)的几种方法--互斥锁,条件变量,信号量,读写锁
Linux下提供了多种方式来处理线程同步,最常用的是互斥锁.条件变量.信号量和读写锁. 下面是思维导图: 一.互斥锁(mutex) 锁机制是同一时刻只允许一个线程执行一个关键部分的代码. 1 . ...
- 关于一点pthread_cond_t条件锁的思考以及实验
转:http://blog.csdn.net/aniao/article/details/5802015 APUE上,关于条件锁.其中有这么几条总结: 1.使用条件锁前必须先锁住对应的互斥锁. 2.条 ...
- Python的条件锁与事件共享
1:事件机制共享队列: 利用消息机制在两个队列中,通过传递消息,实现可以控制的生产者消费者问题要求:readthread读时,writethread不能写:writethread写时,readthre ...
随机推荐
- weixueyuan-Nginx HTTP模块3
https://www.weixueyuan.net/nginx/http/ Nginx镜像模块:ngx_http_mirror_module Nginx 中镜像模块的功能是将用户的访问请求镜像复制到 ...
- 我的程序库:HiCSDB
HiCSDB是我写的一个通用程序库,地址:https://github.com/xumingxsh/HiCSDB 该库的目标是简化C#的数据库交互操作. 在这个库中,我将数据库的交互操作抽象为一下几个 ...
- Drawable图形定制
设置背景设置背景 button或者textview我们想要自定义他的背景就需要用到Drawable中创建xml文件 例如 shape标签是用来控制背景的形状的 他下面的子标签有 stroke控制描边 ...
- 蝉联第一,天翼云电脑持续领跑中国DaaS市场!
近日,IDC发布的<中国虚拟桌面软件及云服务市场半年跟踪报告>数据显示,中国电信天翼云在2023年上半年中国桌面即服务(Desktop as a Service,以下简称"Daa ...
- Jenkins执行Shell脚本超时错误处理指南
Jenkins执行Shell脚本超时错误处理指南 在使用Jenkins进行自动化测试时,经常会遇到需要执行Shell脚本的情况.然而,当Shell脚本执行时间过长,超过了Jenkins配置的超时限制时 ...
- 耳分解、双极定向和 P9394 Solution
耳分解 设无向图 \(G'(V',E')\subset G(V,E)\),简单路径或简单环 \(P:x_1\to \dots \to x_k\) 被称为 \(G\) 关于 \(G'\) 的耳,当且仅当 ...
- 【杂谈】主键ID如何选择——自增数 OR UUID?
1.生成位置如何影响选择? 数据库往返时间 使用自增数时,ID是由数据库在执行INSERT操作时生成的:而UUID则可以在应用层生成. 考虑这样的场景: 一个方法需要插入A和B两个实体.其中B的数据需 ...
- [ABC227E] Swap 题解
考试一道题题解. 30pts:枚举所有串,\(check\) 是否可行. 60pts:做 \(bfs\),暴力推演变化过程,用 \(map\) 去重,时间复杂度 \(O(nC_{n}^{n/3}C_{ ...
- 【小技巧】如何使上线前初始化数据的SQL脚本可测试?
背景 有时候,我们在上线某些特性时,可能需要提前初始化数据,才能使功能正常运作. 初始化数据,常用的有以下两种途径: 单纯为了这个初始化数据,开发一个Excel文件导入功能 开发人员维护SQL语句初始 ...
- TOGAF 内容元模型综合指南
介绍 开放群组架构框架 (TOGAF) 是一种广泛使用的企业架构框架,它提供了一种结构化的方法来设计.规划.实施和管理企业信息技术架构.TOGAF 内容元模型是该框架的重要组成部分,它提供了一种标准化 ...