3.2.4 pthread_cond_wait函数原型:int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
头 文 件:#include <pthread.h>
功 能:用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast()来唤醒它
返 回 值:成功返回 0,失败返回错误码
函数 pthread_cond_wait 必须与 pthread_mutex_t 配套使用 。pthread_cond_wait() 一旦进入 wait 状态就会主动调用 pthread_mutex_unlock() 释放掉 mutex 。当其他线程通过 pthread_cond_signal() 或 pthread_cond_broadcast() 把该线程唤醒,使 pthread_cond_wait() 返回时,该线程又主动调用 pthread_mutex_lock() 来获取该 mutex 。3.2.5 pthread_cond_signal函数原型:int pthread_cond_signal(pthread_cond_t *cond);
头 文 件:#include <pthread.h>
功 能:发送一个信号给另外一个正在处于阻塞等待状态的线程 , 使其脱离阻塞状态
返 回 值:成功返回 0,失败返回错误码
使用 pthread_cond_signal 一般不会有「惊群现象」产生,它最多只给一个线程发信号 。假如有多个线程正在阻塞等待着这个条件变量的话,那么是根据各等待线程优先级的高低确定哪个线程先接收到信号并开始继续执行 。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号 。但无论如何一个 pthread_cond_signal() 调用最多发信一次 。3.2.6 pthread_cond_broadcast函数原型:int pthread_cond_broadcast(pthread_cond_t *cond);
头 文 件:#include <pthread.h>
功 能:唤醒等待该条件的所有线程
返 回 值:成功返回 0,失败返回错误码
这两个函数 pthread_cond_broadcast() 和 pthread_cond_signal 用于通知线程条件变量已经满足条件(变为真) 。在调用这两个函数时,是在给线程或者条件发信号 。3.3 如何使用条件变量我们对「2.3」中的函数 func2 做个简单的修改:
#define MAX 3void *func2(void *arg){ pthread_mutex_lock(&mutex); puts("线程 th2 抢到锁,开始执行 giNum *= 2"); int i; for (i = 1; i <= MAX; i++) { giNum *= 2; } pthread_mutex_unlock(&mutex); return NULL;}对 func2() 做了个微小的改动:将giNum++修改为了giNum *= 2 。
这样的话,线程抢到锁的顺序不同会影响giNum的最终结果:
- th1 先抢到锁:giNum 先执行加操作,然后在执行乘操作,最终结果为 24
- th2 先抢到锁:giNum 先执行乘操作,然后在执行加操作,最终结果为 3
文章插图
如果如何才能做到线程 th1 总是能够先抢到锁呢?下面我们通过条件变量的方式来实现这一想法 。
#include <stdio.h>#include <pthread.h>#include <unistd.h>#define MAX 3#define TRUE 1#define FALSE 0int giNum = 0;int giFlag = FALSE; // TRUE:执行线程 2 的乘操作pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 静态初始化锁pthread_cond_t cond = PTHREAD_COND_INITIALIZER; // 静态初始化条件变量void *func1(void *arg){ pthread_mutex_lock(&mutex); puts("线程 th1 抢到锁"); puts("线程 th1 开始执行 giNum++"); int i; for (i = 1; i <= MAX; i++) { giNum++; } giFlag = TRUE; // 修改 giFlag 的值,使得线程 th2 满足条件 pthread_cond_signal(&cond); // 向线程 th2 发出信号 pthread_mutex_unlock(&mutex); return NULL;}void *func2(void *arg){ pthread_mutex_lock(&mutex); puts("线程 th2 抢到锁"); while (FALSE == giFlag) // 不满足线程 th2 的执行条件 { puts("线程 th2 不满足条件,等待~"); pthread_cond_wait(&cond, &mutex); // 等待被触发 } puts("线程 th2 满足条件,开始执行 giNum *= 2"); int i; for (i = 1; i <= MAX; i++) { giNum *= 2; } pthread_mutex_unlock(&mutex); return NULL;}int main(){ pthread_t th1; pthread_create(&th1, NULL, func1, NULL); pthread_t th2; pthread_create(&th2, NULL, func2, NULL); pthread_join(th1, NULL); pthread_join(th2, NULL); printf("giNum = %d\n", giNum); return 0;}
推荐阅读
- 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 题解