4. 阻塞线程
#include <pthread.h> struct timespec {
time_t tv_sec;/* seconds */ // 秒 longtv_nsec; /* nanosecondes*/ // 纳秒
}
| //time_tcur=time(NULL); | //获取当前时间。 |
|---|---|
| //structtimespect; | //定义timespec结构体变量t |
| //t.tv_sec=cur+1; | //定时1秒 |
// pthread_cond_timedwait(&cond, &t);
/**
- 阻塞等待一个条件变量
*1. 阻塞等待条件变量cond(参1)满足
| * | 2.释放已掌握的互斥锁(解锁互斥量)相当于pthread_mutex_unlock(&mutex); |
|---|---|
| * | ((1),(2)两步为一个原子操作) |
| * | 3.当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁 |
pthread_mutex_lock(&mutex);
- @param cond 指向要初始化的条件变量指针.
- @param mutex 互斥锁.
- @return成功: 0; 失败: 非0错误号.
*/ int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
/**
- 限时等待一个条件变量.
- @param cond 指向要初始化的条件变量指针.
- @param mutex 互斥锁.
- @param abstime 绝对时间.
- @return成功: 0; 失败: 非0错误号.
*/ int pthread_cond_timedwait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex, const struct *restrict abstime);
- 条件变量流程分析
1、条件变量的优缺点
相较于mutex而言,条件变量可以减少竞争。
如直接使用mutex,除了生产者、消费者之间要竞争互斥量以外,消费者之间也需要竞争互斥量;
但如果汇聚(链表)中没有数据,消费者之间竞争互斥锁是无意义的。
有了条件变量机制以后,只有生产者完成生产,才会引起消费者之间的竞争。提高了程序效率。
代码如下:
#include <pthread.h> #include <stdio.h>
#include <stdlib.h> #define NUM_OF_TASKS 3
#define MAX_TASK_QUEUE 11 char tasklist[MAX_TASK_QUEUE]="ABCDEFGHIJ";
int head = 0;
int tail = 0;
int quit = 0;
pthread_mutex_t g_task_lock;
pthread_cond_t g_task_cv;
void *coder(void *notused)
{ pthread_t tid = pthread_self();
while(!quit){ pthread_mutex_lock(&g_task_lock);
while(tail == head){ if(quit){
pthread_mutex_unlock(&g_task_lock);
pthread_exit((void *)0);
} printf("No task now! Thread %u is waiting!\n", (unsigned int)tid);
pthread_cond_wait(&g_task_cv, &g_task_lock);
printf("Have task now! Thread %u is grabing the task !\n", (unsigned int)tid);
} char task = tasklist[head++];
pthread_mutex_unlock(&g_task_lock);
printf("Thread %u has a task %c now!\n", (unsigned int)tid, task);
sleep(5);
printf("Thread %u finish the task %c!\n", (unsigned int)tid, task);
} pthread_exit((void *)0);
}
int main(int argc, char *argv[])
{ pthread_t threads[NUM_OF_TASKS];
int rc;
int t;
pthread_mutex_init(&g_task_lock, NULL);
pthread_cond_init(&g_task_cv, NULL);
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
} }
sleep(5);
for (t=1;t<=4;t++) { pthread_mutex_lock(&g_task_lock);
tail+=t;
printf("I am Boss, I assigned %d tasks, I notify all coders!\n", t);
pthread_cond_broadcast(&g_task_cv);
pthread_mutex_unlock(&g_task_lock);
sleep(20);
} pthread_mutex_lock(&g_task_lock);
quit = 1;
pthread_cond_broadcast(&g_task_cv);
pthread_mutex_unlock(&g_task_lock);
pthread_mutex_destroy(&g_task_lock);
pthread_cond_destroy(&g_task_cv);
pthread_exit(NULL);
}
下面是对于每个输出的分析:
No task now! Thread 1461831424 is waiting!
No task now! Thread 1453438720 is waiting!
No task now! Thread 1445046016 is waiting!
这是主线程新建的三个工作线程,这时候工作线程都是先获得锁,判断tail==head,阻塞,cond_wait释放了锁,三个工作线程都等待信号来唤醒
I am Boss, I assigned 1 tasks, I notify all coders!
主线程增加了一个任务,引起了head!=tail,这时候broadcast唤醒了所有工作线程,之后主线程释放锁,开始睡眠 20s
Have task now! Thread 1453438720 is grabing the task !
1工作线程在竞争中抢到了锁,退出了while(tail==head)循环,并开始完成A任务,这时候把头加1,释放锁
Have task now! Thread 1445046016 is grabing the task !
No task now! Thread 1445046016 is waiting!
Thread 1453438720 has a task A now!
调度程序调度到1线程执行,打印了这句话,开始睡眠5s
Have task now! Thread 1461831424 is grabing the task !
No task now! Thread 1461831424 is waiting!
3工作线程抢到了锁(虽然这时候只有它在抢锁),3得到了锁,但是这时候已经没有工作了。while(tail==head)循环没有退出,因此释放锁,等待唤醒
Thread 1453438720 finish the task A!
No task now! Thread 1453438720 is waiting!
20工作线程执行完任务,这时候还是没有工作。先上锁,后进入while(tail==head)循环没有退出,因此释放锁,等待唤醒
I am Boss, I assigned 2 tasks, I notify all coders!
主线程醒了,又找了两个工作B,C,唤醒了所有工作线程去做
Have task now! Thread 1461831424 is grabing the task !
Thread 1461831424 has a task B now!
24号线程抢到了锁,执行头加1,释放锁,然后睡眠了
Have task now! Thread 1445046016 is grabing the task !
Thread 1445046016 has a task C now!
16号线程抢到了锁,执行头加1,释放锁,然后睡眠了
Have task now! Thread 1453438720 is grabing the task !
No task now! Thread 1453438720 is waiting!
20号线程抢到了锁,没任务了,就释放锁,等待唤醒
Thread 1445046016 finish the task C!
No task now! Thread 1445046016 is waiting!
调度16号线程执行,16号线程完成任务,得到锁,发现没任务,释放锁,等待唤醒
Thread 1461831424 finish the task B!
I am Boss, I assigned 3 tasks, I notify all coders!
主线程醒了,又找了三个个工作D,E,F,唤醒了所有工作线程去做
Have task now! Thread 1461831424 is grabing the task !
Thread 1461831424 has a task D now!
24号线程抢到了锁,执行头加1,释放锁,然后睡眠了
Have task now! Thread 1445046016 is grabing the task !
Thread 1445046016 has a task E now!
16号线程抢到了锁,执行头加1,释放锁,然后睡眠了
Have task now! Thread 1453438720 is grabing the task !
Thread 1453438720 has a task F now!
20号线程抢到了锁,执行头加1,释放锁,然后睡眠了
Thread 1461831424 finish the task D!
No task now! Thread 1461831424 is waiting!
调度24号线程执行,24号线程完成任务,得到锁,发现没任务,释放锁,等待唤醒
Thread 1445046016 finish the task E!
No task now! Thread 1445046016 is waiting!
调度16号线程执行,16号线程完成任务,得到锁,发现没任务,释放锁,等待唤醒
Thread 1453438720 finish the task F!
No task now! Thread 1453438720 is waiting!
调度20号线程执行,20号线程完成任务,得到锁,发现没任务,释放锁,等待唤醒
I am Boss, I assigned 4 tasks, I notify all coders!
主线程醒了,又找了三个个工作G,H,I,J,唤醒了所有工作线程去做
Have task now! Thread 1453438720 is grabing the task !
Thread 1453438720 has a task G now!
24号线程抢到了锁,执行头加1,释放锁,然后睡眠了
Have task now! Thread 1445046016 is grabing the task !
Thread 1445046016 has a task I now!
16号线程抢到了锁,执行头加1,释放锁,然后睡眠了
Thread 1453438720 finish the task G!
Thread 1453438720 has a task J now!
20号线程执行完任务,下一轮while中,持有了锁,发现还有任务,就继续做J,执行头加1,释放了锁,开始睡眠 5s
Thread 1461831424 finish the task H!
No task now! Thread 1461831424 is waiting!
调度24号线程执行,24号线程完成任务,得到锁,发现没任务,释放锁,等待唤醒
Thread 1445046016 finish the task I!
No task now! Thread 1445046016 is waiting!
调度16号线程执行,16号线程完成任务,得到锁,发现没任务,释放锁,等待唤醒
Thread 1453438720 finish the task J!
No task now! Thread 1453438720 is waiting!
20线程完成任务,下一轮while中持有锁,发现没任务了,释放锁,等待唤醒主线程抢锁,设置退出标志quit为1,并通知三个工作线程
Have task now! Thread 1461831424 is grabing the task !
Have task now! Thread 1445046016 is grabing the task !
Have task now! Thread 1453438720 is grabing the task !
三个工作线程分别抢到了锁,发现quit为1,就释放锁,然后终止各自的线程主线程销毁了锁和条件变量
终止了main线程
全站分卷索引 · 230 卷
- 1代码随想录知识星球精华(最强八股文)第五版(计算机基础篇)
- 2DNS
- 3DNS解析过程
- 4递归查询和迭代查询
- 5HTTP特性与简述
- 6HTTP版本演变
- 7HTTP/2
- 8HTTP1.1协议的性能问题
- 9兼容HTTP1.1
- 10二进制帧
- 11并发传输
- 12服务器主动推送资源
- 13总结
- 14HTTP/3
- 15HTTP/2的缺点
- 16QUIC协议的特点
- 17HTTP/3协议
- 18HTTP缓存
- 19协商缓存
- 20HTTPS
- 21HTTPS的特点
- 22非对称加密
- 23摘要算法+数字签名
- 24数字证书
- 25证书信任链
- 26HTTPS是怎么建立连接的
- 27TCP与UDP头部格式
- 28TCP
- 29列举TCP选项
- 30TCP三次握手
- 31为什么需要三次握手?
- 32什么是半连接队列
- 33什么是SYN攻击
- 34为什么挥手需要四次
- 35TIME_WAIT 过多有什么危害?
- 36TCP的滑动窗口
- 374. 程序是如何表示发送方的四个部分的呢?
- 386. 接收窗口和发送窗口的大小是相等的吗?
- 391. 慢启动
- 403. 拥塞发生
- 414. 快速恢复
- 42IP基础
- 43IP地址
- 44在浏览器中输入URL并按下回车之后会发生什么
- 45TCP 抓包实践
- 46TCP连接与断开优化
- 47TCP内核参数
- 48主动方的优化
- 49被动方的优化
- 50Linux是如何收发网络包的
- 51收发流程
- 52面试题
- 53TCP/IP四层网络模型
- 54五层网络体系结构
- 55HTTP响应报文
- 56HTTP常见字段
- 57什么是TCP粘包问题?如何解决TCP粘包问题?
- 58UDP会不会产生粘包问题呢?
- 59请求报文
- 60时间消耗
- 61HTTP缓存有几种
- 62HTTP1.0和HTTP1.1的区别?
- 63HTPP1.1和HTTP1.0有什么区别
- 64HTTP/3
- 65HTTPS是如何建立连接的
- 66DNS是什么
- 67递归查询和迭代查询
- 68TCP与UDP的区别
- 69TCP和UDP分别在什么场景使用?
- 70三次握手的过程
- 71Keep-Alive的优缺点
- 72CDN的工作流程
- 73Session的工作原理
- 74TCP重传机制是怎么实现的?
- 75什么是SS0
- 76HTTP优化方案
- 77浏览器地址栏输入URL回车后涉及到的流程
- 781.5 浏览器发送HTTP/HTTPS请求到web服务器
- 79SYN攻击
- 80操作系统
- 81操作系统的功能
- 82进程和线程
- 83线程基础
- 84进程调度
- 85进程调度方式
- 86进程通信
- 87消息传递(消息队列)
- 88信号量
- 89信号
- 90线程通信
- 91多线程
- 92进程通信
- 93消息队列
- 94信号
- 95守护进程
- 96互斥与同步
- 974. 互斥锁解锁
- 983. 处理方法
- 99读写锁
- 1002. 销毁读写锁
- 1014. 写锁定
- 1021. 创建条件变量
- 1034. 阻塞线程
- 104信号量
- 1054. V操作
- 1062. 读者-写者问题
- 107存储系统
- 108虚拟内存
- 109内存分段
- 110内存分页
- 111虚拟内存
- 112文件系统
- 1135. 文件访问
- 114目录
- 1154. 目录操作
- 1163. 目录的实现
- 1177. 虚拟文件系统
- 1184. 文件系统性能
- 119磁盘编址
- 120三层抽象
- 121外设管理
- 122Linux
- 123select/poll/epoll的区别和联系
- 124Linux 内存管理
- 125Linux虚拟内存
- 126Linux 信号
- 127Linux设计
- 1283. 各种IO模型的比较
- 129Linux网络
- 130Linux中的软链接和硬链接有什么区别?
- 131什么是中断和异常
- 132信号和信号量有什么区别
- 133Linux中如何查看一个进程
- 134进程、线程的区别?
- 135进程之间的通信方式
- 136如何解除死锁
- 137分段和分页的区别
- 138IO多路复用
- 139MySQL表连接
- 140过滤条件
- 141执行一次update语句,发生了什么
- 142用户账号存储
- 143用户账号删除
- 144索引
- 145索引有哪些类别
- 1462. 按照存储分类
- 1475. 按照字段个数分类
- 148索引区分度
- 149索引什么时候会失效
- 150事务的四大特性ACID
- 151并行事务会出现什么问题
- 152幻读是如何解决的
- 153事务隔离的实现
- 154读已提交是怎么实现的
- 155锁
- 1561、全局锁
- 1573、行级锁
- 158锁的划分
- 1592.、程序员角度
- 160Innodb使用表锁还是行锁
- 161什么是binlog
- 162执行引擎
- 163MySQL常见问题
- 1642. 事务
- 1653. 脏页
- 1664. 表结构
- 1676. 查询效率
- 168MySQL分库分表
- 169垂直分表
- 170水平分库
- 171分库分表的问题
- 172数据库排序思路
- 173MySQL主从复制
- 1745. 主从复制模式
- 175面试题
- 176执行一条select语句,发生了什么?
- 177你对关系范式有哪些了解
- 178数据库
- 179Redis基础
- 180Redis单线程为什么还这么快
- 181Redis底层数据结构
- 182Redis持久化
- 183AOF日志
- 184RDB快照
- 185Redis功能
- 1862. 哨兵模式
- 1873. 切片集群
- 188Redis哨兵机制是什么
- 189什么是集群脑裂
- 190过期删除策略有哪些
- 191Redis的LRU算法和LFU算法有什么区别
- 192什么是缓存击穿
- 193如何保证删除缓存操作一定能成功?
- 194为什么Redis是单线程
- 195Redis底层使用了什么数据结构
- 196Redis实现分布式锁
- 197缓存中的热点数据和冷数据是什么
- 198缓存雪崩、击穿、穿透和解决办法【常问】
- 199Redis过期删除策略有哪些
- 200关键概念
- 201单例模式的应用场景
- 202工厂模式
- 203实际应用
- 204抽象工厂模式
- 205概念
- 206应用
- 207实现步骤
- 208主要角色
- 209代理模式应用
- 210策略模式
- 211示例
- 212装饰模式
- 213主要角色
- 21423种设计模式的概念
- 215创建型模式
- 216行为型模式
- 217设计原则
- 218迪米特法则
- 219你在实际开发中用过哪些设计模式
- 220依赖倒置
- 221代理模式和适配器模式有什么区别
- 222固定长度编码
- 223冯诺依曼模型
- 224总线
- 225输入/输出设备
- 226编译系统如何工作
- 227处理器解释指令
- 228场景题 & 系统设计
- 229如何设计一个本地缓存
- 230如何设计一个RPC框架