Linux实现生产者消费者模型
生产者消费者模型
简单来说就是“321原则(并非某一规则,而是为了理解生产者消费者模型)”
“3”代表的是三种关系
生产者与消费者的互斥与同步关系
生产者与生产者的互斥(或竞争)关系
消费者与消费者的互斥(或竞争)关系
“2”代表两种角色
生产者:往交易场所放东西(在计算机中一般都是数据)的人
消费者:从交易场所取东西的人
“1”代表一个交易场所
所谓交易场所就是内存中具有存储数据的一段有界缓冲区
综上,给出生产者消费者模型的描述:两个进程共享一个缓冲区,一个进程称为生产者向缓冲区中放数据,另一个称为消费者从缓冲取中取数据,当缓冲区中被放时,生产者进程就必须可进入挂起状态,直到消费者从缓冲中取走数据时,生产者才能继续向缓冲区中存放数据,同样当缓冲取中没有数据时,消费者进程就必须进入挂起休眠状态,直到生产者向缓冲区中放入数据时,消费者才能被唤醒继续从缓冲区中取走数据。
图示:
基于链表的生产者消费者模型
分析:当使用链表来模拟生产者消费者模型时,我们可以借助链表的插入来扮演生产者的角色,用链表的删除来充当消费者的角色,为了便于实现,我们直接采用链表的头插和链表的头删操作来模拟放数据和取数据这两个过程。
(1)条件变量接口说明
int pthread_cond_wait(pthread_cond_t* redtrist cond , pthread_mutex_t* redtrist )//挂起等待
int pthread_cond_signal(pthread_cond_t* cond ) //唤醒
1、生产者生产好数据之后通知消费者来消费数据
2、消费者消费完数据后通知生产者前来生产
(2)锁相关接口说明
int pthread_mutex_lock(pthread_mutex_t *mutex);
阻塞式加锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);
非阻塞式加锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
解锁,无论是阻塞式的加锁还是非阻塞式的加锁都需要使用此函数进行解锁
基于单链表的生产者消费者模型实现
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>typedef struct list
{int data;struct list* next;
}list,*plist,**pplist;plist head;pthread_cond_t cond=PTHREAD_COND_INITIALIZER;plist alloc_node(int d,plist l)
{plist tmp=(plist)malloc(sizeof(list));if(!tmp){perror("malloc");exit(1);}tmp->data=d;tmp->next=l;return tmp;
}void initlist(pplist l){*l=alloc_node(0,NULL);
}int isempty(plist l){return l->next==NULL?1:0;
}void free_node(plist l){if(l!=NULL){free(l);l=NULL;}
}void push_front(plist l,int d){plist tmp=alloc_node(d,NULL);tmp->next=l->next;l->next=tmp;}void pop_front(plist l,int* out)
{if(!isempty(l)){plist tmp=l->next;l->next=tmp->next;*out=tmp->data;free_node(tmp);}
}void showlist(plist l)
{plist start=l->next;while(start){printf("%d ",start->data);start=start->next;}printf("\n");
}void destroy(plist l){int data;while(!isempty(l)){pop_front(l,&data);}free_node(l);
}void* consume(void* arg){pthread_mutex_t* lockp=(pthread_mutex_t*)arg;int data=0;while(1){pthread_mutex_lock(lockp);if(isempty(head)){pthread_cond_wait(&cond,&lockp);}pop_front(head,&data);printf("consum done: %d\n",data);pthread_mutex_unlock(lockp);pthread_cond_signal(&cond);}
}
void* product(void* arg){pthread_mutex_t* lockp=(pthread_mutex_t*)arg;int data=0;while(1){pthread_mutex_lock(lockp);data=rand()%1234;push_front(head,data);printf("product done: %d\n",data);pthread_mutex_unlock(lockp);pthread_cond_signal(&cond);}
}int main()
{pthread_mutex_t lock;pthread_mutex_init(&lock,NULL);initlist(&head);pthread_t consumer,producter;pthread_create(&consumer,NULL,consume,(pthread_mutex_t *)&lock);pthread_create(&producter,NULL,product,(pthread_mutex_t *)&lock);pthread_join(consumer,NULL);pthread_join(producter,NULL);destroy(head);pthread_mutex_destroy(&lock);return 0;
}
基于多元信号量的生产者消费者
背景知识
涉及到信号量的知识,首先我们需要搞清楚的一个东西就是PV操作的含义,PV操作是由P操作原语和V操作原语组成的(原语很好理解,就是任务要么全部做完,要么都不做),对于信号量的具体操作如下所示:
P(S):
(1)将信号量S的值进行减1操作;
(2)如果S>=0则该进程继续执行,否则该进程就会被挂起等待,直到有进程释放了,才可以被唤醒;
V(S):
(1)与P操作正好相反,V操作是将信号量的值加1;
(2)如果S>0,则该进程继续执行,否则释放队列中第一个等待信号量的进程。
PV操作的意义:用信号量及PV操作,实现进程的同步与互斥,PV操作属于进程的低级通信。
在使用型号量来模拟生产者和消费者模型时,我们是环形队列来实现生产者和消费者这两种角色之间的对于资源的存取,首先我们知道队列的底层存储数据的方式其实就是一个数组,只要控制好对于队头和队尾的相关计算,我们就可以实现循环队列,而生产者依旧是在有空间的时候进行存放数据,没有空间时进入挂起等待状态,消费者则是在有数据时进行取数据,没有数据时进行挂起等待操作,这样我们便可以实现生产者和消费模型
不过先不要高兴的太早了,我们还需要制定这个规则才能实现:
生产者优先:其实就算消费者优先,由于刚开始没有生产出数据,消费者也会被挂起;
消费者永远不能追上消费者:试想一下如果消费者追上生产者或者超过消费者的时候,此时消费者消费的并不是生产者实际所生产出的数据,而属于垃圾数据;
生产者不能将消费者包一圈:这个也很好理解,如果生产者允许将消费者包一圈的话,那就相当于生产者可以无限的生产,并不停的覆盖掉原来所产生的数据,那么如果原来生产出的数据中如果有的是消费者需要获取的数据,那么除了生产者在次生产出该数据外,消费者将再也不能得到所想要的数据;
图示:
代码实现:
#include<stdio.h>
#include<pthread.h>
#include<semaphore.h>int ring[64];
sem_t semBlank;
sem_t semData;void* consume(void* arg){int step=0;while(1){sem_wait(&semData);int data=ring[step];step++;step%=64;printf("consume done: %d\n",data);sem_post(&semBlank);}
}void* product(void* arg){int step=0;while(1){sem_wait(&semBlank);int data=rand()%1234;ring[step]=data;step++;step%=64;printf("produce done: %d\n",data);sem_post(&semData);}
} int main()
{ sem_init(&semBlank,0,64);sem_init(&semData,0,0);pthread_t consumer,producter;pthread_create(&consumer,NULL,consume,NULL); pthread_create(&producter,NULL,product,NULL);pthread_join(consumer,NULL);pthread_join(producter,NULL);sem_destroy(&semBlank);sem_destroy(&semData);return 0;
}
运行结果:
Linux实现生产者消费者模型相关推荐
- [Linux]生产者消费者模型(基于BlockQueue的生产者消费者模型 | 基于环形队列的生产者消费者模型 | 信号量 )
文章目录 生产者消费者模型 函数调用角度理解生产者消费者模型 生活角度理解生产者消费者模型 为什么要使用生产者消费者模型 生产者消费者模型优点 321原则 基于BlockingQueue的生产者消费者 ...
- Linux多线程——生产者消费者模型
目录 一.生产者消费者模型 1.1 什么是生成者消费者模型 1.2 生产者消费者模型的优点 1.3 基于阻塞队列实现生产者消费者模型 1.4 POSIX信号量 1.4.1 信号量概念 1.4.2 P操 ...
- 【Linux】生产者消费者模型
文章目录 一. 什么是生产者消费者模型 1. 基本概念 2. 三种关系 3. 再次理解生产者消费者模型 二. 生产者消费者模型的优点 三. 基于BlockingQueue的生产者消费者模型 1. 准备 ...
- 【Linux】生产者消费者模型-基于环形队列实现
1.环形缓冲区的优势 在上篇博客基于阻塞队列的生产者消费者模型中我介绍了什么是生产者消费者模型以及生产者-消费者模式,还没了解的可以戳链接查看. 基于阻塞队列的实现,虽然简单,但是对内存分配性能要求较 ...
- linux知识(二)互斥量、信号量和生产者消费者模型
linux知识(二)互斥量.信号量和生产者消费者模型 一.互斥量 产生原因 二.信号量 生产者消费者模型 一.互斥量 产生原因 使用多线程常常会碰到数据混乱的问题,那么使用互斥量,相当于"加 ...
- Linux 多线程编程(实现生产者消费者模型)
Linux 多线程编程 线程分类 线程按照其调度者可以分为用户级线程和内核级线程两种. 内核级线程 在一个系统上实现线程模型的方式有好几种,因内核和用户空间提供的支持而有一定程度的级别差异.最简单的模 ...
- 【Linux入门】多线程(线程概念、生产者消费者模型、消息队列、线程池)万字解说
目录 1️⃣线程概念 什么是线程 线程的优点 线程的缺点 线程异常 线程异常 Linux进程VS线程 2️⃣线程控制 创建线程 获取线程的id 线程终止 等待线程 线程分离 3️⃣线程互斥 进程线程间 ...
- Linux基于单链表环形队列的多线程生产者消费者模型
生产者–消费者模型简述 对于生产者–消费者模型,相信我们都不陌生,因为生活中,我们无时无刻不在扮演生产者或消费者.但是对于Linux中的生产者–消费者模型,大家又了解了一个什么程度? 其实,说白了就是 ...
- 【Linux篇】第十六篇——生产者消费者模型
生产者消费者模型 生产者消费者模型的概念 生产者消费者模型的特点 生产者消费者模型优点 基于BlockingQueue的生产消费者模型 基于阻塞队列的生产者消费者模型 模拟实现基于阻塞队列的生产消费模 ...
最新文章
- 通过脚本案例学习shell(五) 通过创建DNS脚本一步一步教你将一个普通脚本规范到一个生产环境脚本...
- 一键获取解锁码_Windows 骚操作:轻轻一按手机指纹解锁电脑!
- python【力扣LeetCode算法题库】8-字符串转换整数 (atoi)
- 启明云端分享| SSD201 / SSD202D 在IP广播的应用
- Cell Research | 单细胞测序技术揭示派杰氏病的致病机制
- 前端开发 什么是网页 什么是html
- SkyEye仿真平台下的操作系统实验- 准备篇(一)
- SpringCloud工作笔记103---Mybatis-plus使用注解 @TableField(exist = false) 注明非数据库字段属性
- 使用ffmpeg快速生成视频截图
- python函数式编程:apply, map, lambda和偏函数
- Python实现(百度地图API)地理编码与逆地理编码
- PyQt5 QLabel控件
- 昆明计算机学校录取分数,云南省昆明铁路机械学校2021年招生录取分数线
- 阿里云服务(四)—云数据库RDS
- 360手机刷机:360N7pro刷机
- python制作冰花_小学作文制作冻冰花
- freeipa 客户端 Kerberos认证 SSSD关系
- 仿牛客论坛项目(下)
- vsftpd 升级3.0.2-29 和 增加账号访问
- 修改TCP Timestamp优化传输性能