news 2026/4/16 17:56:40

Linux条件变量:线程同步的利器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux条件变量:线程同步的利器

Linux 条件变量:线程同步的利器

条件变量(Condition Variable)是 POSIX 线程(pthread)库中最重要、最常用的线程间同步原语之一。它与互斥锁(mutex)配合使用,主要解决“等待某个条件成立”的场景,是实现生产者-消费者模型线程池任务等待状态变更通知等经典模式的首选工具。

一、条件变量的核心思想

条件变量的核心语义只有一句话:

“某个线程可以挂起(等待),直到另一个线程通知它‘条件可能已经满足了’”

不保存任何状态,只负责唤醒等待

条件变量必须和互斥锁一起使用,这是铁律。

最经典的使用模式(伪代码):

pthread_mutex_tmutex;pthread_cond_tcond;// 等待条件成立pthread_mutex_lock(&mutex);while(条件不满足){pthread_cond_wait(&cond,&mutex);// 释放锁 → 等待 → 被唤醒后重新加锁}...// 条件满足,处理业务pthread_mutex_unlock(&mutex);// 通知等待者pthread_mutex_lock(&mutex);...// 修改条件pthread_cond_signal(&cond);// 唤醒一个// 或 pthread_cond_broadcast(&cond); // 唤醒全部pthread_mutex_unlock(&mutex);

二、条件变量的核心 API(全家桶)

函数作用是否阻塞必须持有锁?典型使用场景
pthread_cond_init初始化条件变量创建时调用
pthread_cond_destroy销毁条件变量不再使用时
pthread_cond_wait等待条件变量被通知(释放锁)核心等待函数
pthread_cond_timedwait带超时的等待需要超时保护的等待
pthread_cond_signal唤醒至少一个等待线程建议持有单消费者或无差别唤醒
pthread_cond_broadcast唤醒所有等待线程建议持有多消费者、状态广播

三、最经典的正确写法(必须掌握)

1. 生产者-消费者模型(单生产者多消费者)
#include<pthread.h>#include<stdio.h>#include<unistd.h>#include<stdlib.h>#defineBUFFER_SIZE10pthread_mutex_tmutex=PTHREAD_MUTEX_INITIALIZER;pthread_cond_tcond_not_full=PTHREAD_COND_INITIALIZER;pthread_cond_tcond_not_empty=PTHREAD_COND_INITIALIZER;intbuffer[BUFFER_SIZE];intcount=0;intin=0,out=0;void*producer(void*arg){for(inti=0;i<20;i++){pthread_mutex_lock(&mutex);// 等待不满while(count==BUFFER_SIZE){pthread_cond_wait(&cond_not_full,&mutex);}buffer[in]=i;in=(in+1)%BUFFER_SIZE;count++;printf("生产: %d (count=%d)\n",i,count);pthread_cond_broadcast(&cond_not_empty);// 通知消费者pthread_mutex_unlock(&mutex);usleep(rand()%100000);}returnNULL;}void*consumer(void*arg){for(inti=0;i<20;i++){pthread_mutex_lock(&mutex);// 等待不空while(count==0){pthread_cond_wait(&cond_not_empty,&mutex);}intdata=buffer[out];out=(out+1)%BUFFER_SIZE;count--;printf("消费: %d (count=%d)\n",data,count);pthread_cond_signal(&cond_not_full);// 通知生产者pthread_mutex_unlock(&mutex);usleep(rand()%200000);}returnNULL;}intmain(){pthread_tprod,cons;pthread_create(&prod,NULL,producer,NULL);pthread_create(&cons,NULL,consumer,NULL);pthread_join(prod,NULL);pthread_join(cons,NULL);return0;}
2. 为什么 while 而不是 if?(虚假唤醒)

虚假唤醒(spurious wakeup)是条件变量的天然特性:

  • 即使没人 signal,线程也可能被系统唤醒
  • 被唤醒后条件不一定成立

因此必须用 while 循环检查条件,这是 POSIX 标准强烈推荐的写法:

while(条件不满足){pthread_cond_wait(&cond,&mutex);}

永远不要用 if

四、条件变量常见误区与正确姿势

误区后果正确做法
用 if 而不是 while 判断条件虚假唤醒导致逻辑错误永远用 while
在 signal/broadcast 后不解锁可能导致被唤醒线程立即再次阻塞先 signal,再 unlock(或一起在锁内)
忘记初始化条件变量未定义行为总是 pthread_cond_init
在不持有锁的情况下 signal可能丢失唤醒建议在持有锁时 signal
使用同一个 cond 变量做多种等待唤醒错乱每种等待条件用独立的 cond
忘记销毁 cond 和 mutex资源泄漏程序结束前 destroy

五、进阶技巧与工程实践

  1. 带超时的等待(防止永久阻塞)
structtimespects;clock_gettime(CLOCK_REALTIME,&ts);ts.tv_sec+=5;// 超时 5 秒pthread_cond_timedwait(&cond,&mutex,&ts);
  1. 多条件等待(经典写法)
pthread_mutex_lock(&mutex);while(!ready||!data_available){if(!ready)pthread_cond_wait(&cond_ready,&mutex);if(!data_available)pthread_cond_wait(&cond_data,&mutex);}
  1. 条件变量 + 互斥锁的最佳实践命名
pthread_mutex_tqueue_mutex;pthread_cond_tqueue_not_empty;pthread_cond_tqueue_not_full;
  1. 线程池中任务等待的典型用法
// 线程池 worker 线程while(1){pthread_mutex_lock(&pool->mutex);while(queue_empty(pool->task_queue)){pthread_cond_wait(&pool->cond,&pool->mutex);}task=dequeue(pool->task_queue);pthread_mutex_unlock(&pool->mutex);task->func(task->arg);}

六、总结:一句话记住条件变量

条件变量不是用来“保存条件”的,而是用来“通知条件可能变了”的。
它必须搭配互斥锁使用,等待时用 while + cond_wait通知时用 signal 或 broadcast

掌握了条件变量 + 互斥锁,你就真正掌握了线程同步的精髓,几乎所有复杂的多线程协作模式(线程池、任务队列、状态机、缓存失效通知、barrier 等)都可以基于它构建。

如果你想看更复杂的例子(比如多生产者多消费者、带优先级的条件等待、实现线程安全的队列、条件变量在实际项目中的封装),可以直接告诉我,我可以继续给出完整代码和详细说明。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 15:55:15

2026年BI 选型看这一篇就够了!深度测评十大BI报表工具

目录 一、FineBI 二、瓴羊 Quick BI 三、Microsoft Power BI 四、Tableau Pulse 五、Qlik Sense 六、观远数据 BI 七、思迈特 SmartBI 八、永洪 BI 九、DataEase 十、Apache Superset BI工具总结与对比表 常见问答Q&A 做数据工作这些年&#xff0c;帮很多企业做…

作者头像 李华
网站建设 2026/4/16 14:41:04

MITSUBISHI三菱 RA60H3847M1-501 SMD 线性稳压器(LDO)

特性 增强型MOSFET晶体管(Idd≈OAVds12.5V, VGd0V) 输出功率>60W&#xff0c;效率>40%Vo12.5V, VDD5V, Pin50mW 宽带频率范围:378-470MHz 金属屏蔽结构&#xff0c;使杂散发射的改进变得简单 模块尺寸:67x19.4x9.9 mm 通过设置栅极电压来调节静态漏电流&#xff0c;并利用…

作者头像 李华
网站建设 2026/4/16 14:38:50

SGMICRO圣邦微 SGM5347-8XTS16G/TR TSSOP-16 模数转换芯片ADC

特性 低功耗&#xff08;0.5mW/通道&#xff09;集成 8 通道 8 位 DAC内置模拟输出放大器&#xff1a;带短路电流控制的灌/拉电流菊花链操作8 通道输出同时更新独立通道掉电功能&#xff1a;掉电模式下 ICC 典型值为 0.6μA通过分离 MCU 接口和运算放大器的电源与 DAC 的电源&…

作者头像 李华
网站建设 2026/4/15 15:10:10

Redis 事务的“原子性”迷思:为什么我们最终选择了 Lua 脚本

写在前面的话 作为一个长期和关系型数据库&#xff08;RDBMS&#xff09;打交道的开发者&#xff0c;初次查阅 Redis 文档时&#xff0c;看到 MULTI、EXEC、DISCARD 这些指令&#xff0c;心中难免涌起一股由于熟悉而带来的安全感。 我们的大脑会自动建立映射&#xff1a;MULTI …

作者头像 李华
网站建设 2026/4/16 7:20:58

AI红队革命:一键渗透的技术临界点与未来演进图景

开篇&#xff1a;AI重构攻防规则&#xff0c;一键渗透从愿景走向现实的前夜 当网络安全攻防进入AI原生对抗时代&#xff0c;红队作为网络安全的“实战检验者”&#xff0c;正经历着从“人工驱动”到“智能自主”的根本性变革。传统红队渗透依赖资深专家的经验与技术积累&#x…

作者头像 李华
网站建设 2026/4/16 7:26:04

Excel:筛选两列中不匹配项

在Excel中&#xff0c;有多种高效方法可以找出两列数据中的不匹配项&#xff0c;以下是几种常用且实用的方法&#xff1a; 1. 使用条件格式高亮显示差异这种方法通过颜色直观地标出两列中不一致的单元格&#xff0c;适合快速视觉检查。选中需要比较的第一列数据区域&#xff08…

作者头像 李华