一、为什么有了互斥锁,还需要条件变量?
在前面的学习中,我用互斥锁(Mutex)解决了多线程并发抢资源的“竞态条件”问题。互斥锁确实好用,就像一把门锁,保证同一时间只有一个线程能进房间。
但是,在真实的开发场景中,互斥锁暴露出了一个致命的问题。
假设我们在写一个“生产者-消费者”模型:
· 消费者线程要去仓库拿东西。如果仓库空了,它怎么办?
· 如果没有条件变量,消费者只能疯狂地加锁、检查仓库、发现没货、解锁,然后再加锁、再检查……
· 这种行为在计算机里叫“忙等待(Busy Waiting)”。就像你一直疯狂地敲仓库的门问:“有货吗?有货吗?有货吗?”
这简直是一场灾难!因为消费者线程会疯狂地占用 CPU 时间片去循环判断,导致 CPU 白白空转,极度浪费系统性能。
这时候,条件变量(Condition Variable)就该登场了。
条件变量的本质,就像是在仓库门口装了一个“门铃”。
仓库没货的时候,消费者不用再疯狂敲门了,直接在门口搬个小板凳睡大觉(阻塞等待),把 CPU 让给别人。
等生产者把货放进去,按下门铃(调用通知函数),消费者听到铃声醒过来,再去拿货。
互斥锁保护了数据的安全,而条件变量让线程学会了“优雅地休息”。接下来,我将记录我学习条件变量的核心 API 以及踩坑的过程。
二、核心API
同样的,需要先声明条件变量
pthread_cond_t cod;
1)int pthread_cond_init(pthread_cond_t *__restrict__ __cond, const pthread_condattr_t *__restrict__ __cond_attr) 初始化条件变量
2)int pthread_cond_wait(pthread_cond_t *__restrict__ __cond, pthread_mutex_t *__restrict__ __mutex) 第二个参数是当前需要放弃的互斥锁
wait会做三件事:
a.自动释放 mtx 互斥锁!!→ 不释放的话主线程永远拿不到锁,没法修改ready,死锁!
b. 线程阻塞休眠,让出CPU,不会空轮询浪费CPU
c. 收到signal唤醒之后,自动重新获取mtx锁,函数才返回
3)int pthread_cond_timedwait(pthread_cond_t *__restrict__ __cond, pthread_mutex_t *__restrict__ __mutex, const struct timespec *__restrict__ __abstime) 定时等待,超过指定时间自动返回,避免永久卡死
4)int pthread_cond_signal(pthread_cond_t *__cond) 唤醒至少一个等待该条件变量的线程
5)int pthread_cond_broadcast(pthread_cond_t *__cond) 唤醒所有等待这个条件变量的线程
6)int pthread_cond_destroy(pthread_cond_t *__cond) 销毁对应条件变量
三、生产者-消费者示例



从线程执行顺序可看出,等生产者把货放进去,按下门铃(调用通知函数),消费者听到铃声醒过来,再去拿货,很有序
可以加个is_over标志位,当生产者不再生产,需要通知消费者,避免永久阻塞,这点很重要
注意,修改共享变量一定要上锁!保证互斥访问
5369



被折叠的 条评论
为什么被折叠?



