linux生产者消费者模型---线程池的实现实现

线程池::一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个 线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不 仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内 存、网络sockets等的数量。
使用场景
1需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使 用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于 长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。
2对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
3接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没 有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程 可能使内存到达极限,出现错误

生产者和消费者模型:通过一个容器来解决生产者和消费者的强耦合问题,生产者和消费者彼此直接不直接通讯,而是通过组设队列来进行通讯,所以生产者产生数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里去,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力,这个阻塞队列就是出来给生产者和消费者解耦的
生产者 ------------缓冲区--------------消费者
从网卡抓流量交给缓冲区,从缓冲区获取数据进行处理分析
生产者抓取数据将数据放到缓冲区中,消费者从缓冲区中拿到数据进行处理
Eg:双11一瞬间有一堆流量,接受到就处理,处理速度慢,来不及获取,就会丢掉,上不了淘宝买不到东西,一类线程获取请求不做任何处理,放到缓冲区,另一类线程就是消费者从缓冲区拿数据进行处理–>缓冲区很大(一个队列,)—>空间换时间–>后面消费者慢慢处理—>效率变高了,安全度也高了
缓冲区:
支持忙闲不均(忙闲数据都放在缓冲区–>好处理模块清晰)
解耦和:生产者消费者之间并不直接进行交互,让他们直接没有更多的关联性,只需要缓冲区提供接口,向内添加数据和取数据—>相关程度降低
支持并发,如果没有缓冲区一个生产者对应一个消费者(1对1的情况)有了缓冲区,完全可以实现多对1(多个生产者放数据在缓冲区一个消费者慢慢处理),多对多;
缓冲区是个队列–先进先出–排队
队列需要考虑线程安全问题(多对1多对多时候)
一个场所两类角色三种关系
一个场所:缓冲区,两类角色:生产者和消费,三种关系:生产者和生产者关系,生产者和消费者关系,消费者和消费者关系–>描述的是如何保证线程安全
生产者与生产者:具备互斥,(万一放到同一个位置怎么办)
消费者与消费者:互斥(同时取数据取一个怎么办)
生产者和消费者:同步加互斥–>我放数据你不能拿(互斥),我放完了你才能拿(同步)
C++实现生产者与消费者模型–>std::queue队列不是线程安全的–>为什么不设计成线程安全–>如果队列都设计成了线程安全–>有的程序为了提高性能就不加锁(加锁对性能有消耗),为了提高性能空间换时间,就生产者与消费者一对一中间拿一个缓冲区,这样队列中间本身队列就实现了同步,不需要用锁了,时间减少很多
#include<stdint.h>
6 int main(){
W> 7 int i=0;
8 uint64_t a=0;//无符号64位整型数据
9 while(a++<1000000)
10 return 0;
11 }
在这里插入图片描述

在没有加锁之前Time ./a.out运行时间实际使用了0.011秒
加锁后
int main(){
W> 7 int i=0;
8 uint64_t a=0;//无符号64位整型数据
9 pthread_mutex_t mutex;
10 pthread_mutex_init(&mutex,NULL);
11 while(a++<1000000){
12 pthread_mutex_lock(&mutex);
13 pthread_mutex_unlock(&mutex); }
14 return 0;
15 }
在这里插入图片描述
可以看到接近4倍在加锁后,效率消耗挺大,加锁解锁时间a++三个时间,假设t1=a++,t2=加锁,t3=解锁,如果这样只算a++时间发现性能消耗很大
—>高性能程序一般不出现锁
所以std::queue对性能有影响,而且一旦实现线程安全,想用线程不安全的就没法实现–>使用场景减少,所以线程安全可以自己实现–
实现线程安全的队列
需要:1队列2同步与互斥(mutex,cond)–>此处条件变量不能给一个,因为给一个的话生产者和消费者等待在不同的条件变量上面得给两个条件变量—>full+empty
队列内部也是有链表实现–>队列不够可以添加结点扩充队列–>添加结点(如果瞬间来10万结点一次添加太多也不符合内存空间很大–>程序爆炸直接退出)–>需要一个上限–>int capacity
封装的就是以上的,成员函数有哪些:
提供两个接口
(1)向从队列插入数据queuepush
(2)从队列中拿数据queuepop
(3)私有成员函数:判断队列是否满了,生产者等待空了消费者等待QueueIsFull,QueueIsEmpty
如何实现
构造函数:初始化–>不是所有的初始化都要放在里面–>一定要简单,放的基本上不会出错的初始化才会放在里面因为构造函数,因为构造函数无法判断返回值,出错直接断言退出
析构函数
传引用:传递intdata希望将这个值放在data空间里面去才能将值获取出来否则直接传对象本身获取不到
传引用将对象别名传进来,对象本身进行修改,也可以达到传递指针效果
程序如下:
#include
3 #include
4 #include <stdio.h>
5 #include <unistd.h>
6 #include <stdlib.h>
7 #include<pthread.h>
8 #include
9 //封装类
10 class BlockQueue
11 {
12 private:
13 std::queue _list;//队列
14 int _cap;//容量
15 pthread_mutex_t _mutex;//互斥锁
16 pthread_cond_t _product;//条件变量
17 pthread_cond_t _consumer;
18 bool QueueIsFull(){
19 return (_list.size()==_cap ? true : false);//等于容量则满了,不相等没有满
20 }
bool QueueIsEmpty(){
22 return(_list.size()==0 ? true:false);//队列为空
23 }
24 public:
25 BlockQueue(int cap=10): _cap(cap)
26 {//构造函数,默认不传参数为10,给cap值为10里面int cap不能为_cap形参后面_cap才 是成员变量:后面为初始化的参数列表
27 pthread_mutex_init(&_mutex,NULL);
28 pthread_cond_init(&_product,NULL);
29 pthread_cond_init(&_consumer,NULL);
30 }
31 ~BlockQueue()
32 {//析构函数–>退出时候调用–>对象释放时候调用
33 pthread_mutex_destroy(&_mutex);
34 pthread_cond_destroy(&_product);
35 pthread_cond_destroy(&_consumer);
36 }
37 bool QueuePush(int data)
38 {
39 pthread_mutex_lock(&_mutex);
while(QueueIsFull()){
41 printf(“queue is full\n”);
42 pthread_cond_wait(&_product,&_mutex);//入队
43 }
44 _list.push(data);
45 pthread_mutex_unlock(&_mutex);
46 pthread_cond_signal(&_consumer);//通知消费者,唤醒消费者
47 }
48 bool Queuepop(int data){
49 pthread_mutex_lock(&_mutex);
50 while(QueueIsEmpty()){
51 printf(“queue is empty\n”);
52 pthread_cond_wait(&_consumer,&_mutex);//为空等待
53 }
54 data=_list.front();//获取头结点但是不会出队
55 _list.pop();//出队//为什么获取结点和出队不封装在一起为了让使用场景更广泛,有时候获取结点看一下是什么但是并不想出队不是线程安全的,所以要锁
56 pthread_mutex_unlock(&_mutex);
57 pthread_cond_signal(&_product);//唤醒生产者
58 }
};
60 void
thr_consumer(void arg){
61 BlockQueue q=(BlockQueue)arg;//强转
62 while(1){
63 //消费者
64 int data;
65 if(q->Queuepop(&data)){
66 printf(“get data :%d\n”,data);
67 }
68 }
69 return NULL;
70 }
71 void
thr_product(void arg){
72 BlockQueue q=(BlockQueue)arg;//强转
73 while(1){
74 int i=0;//生产者放数据
75 printf(“put data:%d\n”,i);
76 q->QueuePush(i);//i++先使用,打印多少代表放了多少
77 i++;
78 usleep(100000);
}
80 return NULL;
81 }
82 int main(){
83 //创建4个生产者4个消费者进行争抢
84 BlockQueue q;
85 int i,ret;
86 pthread_t consutmertid[4],producttid[4];
87
88 for(i=0;i<4;i++){
89 ret=pthread_create(&consutmertid[i],NULL,thr_consumer,(void
)&q);
90 if(ret!=0){
91 printf(“create thread is errno\n”);
92 return -1;
93 }
94 }
95 for(i=0;i<4;i++){
96 ret=pthread_create(&producttid[i],NULL,thr_product,(void
)&q);
97 if(ret!=0){
98 printf(“create thread is errno\n”);
return -1;
100 }
101 }
102 for(i=0;i<4;i++){
103 pthread_join(producttid[i],NULL);
104 pthread_join(consutmertid[i],NULL);
105 }
106 return 0;
107 }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值