结合下面的伪代码来加深理解:
/* 线程A执行函数 */int giFlag = FALSE; // FALSE:线程A不满足执行条件void *funcA(void *arg){ // pthread_mutex_lock(&mutex); // 传入前不加锁 while (FALSE == giFlag) { // 在调用pthread_cond_wait前,线程B启动并执行了函数funcB,修改条件并发出信号 // 但此时由于线程A还未执行pthread_cond_wait函数,所以会忽略掉线程B发出的信号 // 等到线程A开始执行pthread_cond_wait时,已经收不到来自线程B的信号了,会一直阻塞 pthread_cond_wait(&cond, &mutex); } // ToDo pthread_mutex_unlock(&mutex); return NULL;}/* 线程B执行条件 */void *funcB(void *arg){ pthread_mutex_lock(&mutex); // 线程B将giFlag置为TRUE,并通过cond_signal将信号发送给了线程A giFlag = TRUE; pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); return NULL;}4.2.2 传入后为何要释放
传入后解锁是为了条件能够被改变 。传入后的解锁,是因为调用 pthread_cond_signal 的那部分,需要先加锁更改条件后才调用 pthread_cond_signal(更改条件与等待条件满足,都是针对条件这一个资源的竞争,所以调用 pthread_cond_wait 和调用 pthread_cond_signal 的两个线程需要同一把锁) 。
如果 pthread_cond_wait 内不对 mutex 解锁,那么在调用 pthread_cond_wait 后,其他线程就不能更改条件,条件就会一直不满足 。
4.2.3 返回时又为何再次锁
对于 1,这里的理由与传入 pthread_cond_wait 前锁 mutex 的理由差不多 。如果不锁,那么线程 A 调用 pthread_cond_wait后,条件满足,线程 A 被唤醒,从 pthread_cond_wait 返回 。线程 B 在此时更改了条件,使得条件不满足,线程 A 并不知道条件又被更改,还是以为条件满足,就可能出错 。
- 返回前再次锁 mutex 是为了保证条件从「线程从 pthread_cond_wait 返回后」到「再次条件判断前」不被改变 。
- 使得在「pthread_cond_signal之后」与「pthread_mutex_unlock 之前」可以执行其他的语句 。
对于 2,由于 mutex 在这时已经被这个线程锁住,还没有解锁,所以调用 pthread_cond_wait 的那个线程在 pthread_cond_wait 返回前的锁 mutex 的行为就会阻塞,直到 pthread_cond_signal 后的语句执行完并解锁,pthread_cond_wait 才会返回 。
4.3 pthread_cond_signal 的两种写法由于 pthread_cond_wait 返回前再次锁的行为,所以 pthread_cond_signal 不一定必须放在解锁 mutex之前 。
4.3.1 写法一{ pthread_mutex_lock(&mutex); // ToDo pthread_cond_signal(&cond); // ToDo pthread_mutex_unlock(&mutex);}缺点:在某些线程的实现中,会造成等待线程从内核中被唤醒(接收到了 cond_signal 发出的信号)回到用户空间,然后 pthread_cond_wait 返回前需要加锁,但是发现锁没有被释放,又回到内核空间所以一来一回会有性能的问题 。
但是在 LinuxThreads 或者 NPTL 里面,就不会有这个问题 。因为在 Linux 线程中,有两个队列,分别是 cond_wait 队列和mutex_lock 队列,cond_signal 只是让线程从 cond_wait 队列移到 mutex_lock 队列,而不用返回到用户空间,不会有性能的损耗,所以Linux中这样用没问题 。
经验总结扩展阅读
- VLQ & Base64 VLQ 编码方式的原理及代码实现
- MYSQL-->InnoDB引擎底层原理
- StampedLock:一个并发编程中非常重要的票据锁
- Longchamp龙骧饺子包
- <三>从编译器角度理解C++代码编译和链接原理
- <一>关于进程虚拟地址空间区域内存划分和布局
- GitLab私有化部署 - CI/CD - 持续集成/交付/部署 - 源代码托管 & 自动化部署
- 🔥支持 Java 19 的轻量级应用开发框架,Solon v1.10.4 发布
- Bert不完全手册9. 长文本建模 BigBird & Longformer & Reformer & Performer
- XXI Open Cup, Grand Prix of Belarus 2020-2021 Winter Petrozavodsk Camp, Belarusian SU Contest 题解