生产者–消费者模型简述

  对于生产者–消费者模型,相信我们都不陌生,因为生活中,我们无时无刻不在扮演生产者或消费者。但是对于Linux中的生产者–消费者模型,大家又了解了一个什么程度?
  其实,说白了就是一个生产,一个消费的关系,而且保证生产者在不生产的时候,消费者无法消费(这跟生活中一样,只不过生活中我们是确实消费不到任何东西,但在计算机中,我们就有可能消费到无效数据,、乱数据、过期数据等等);消费者的消费欲望饱和之后,生产者不生产(同上,如果市场中大量涌入某个商品,导致市场急速饱和,那么厂家户很果断的暂时放弃生产这个商品)。但是生活中的又跟计算机中的有区别,在于:一般情况下,消费者之间并无任何关系,但是计算机中,消费者是存在竞争关系的,即A消费者拿走了这个数据,那么剩余的B,C,D,E…..等等都无法使用这个数据,因为已经被消费了。生产者之间当然是竞争关系了,两个供销商为了业绩,会竞争成为一个大型超市的供货对象,很相似的是,计算机中,多个生产者都是为了为这块空间生产数据(写数据),为了空间的使用权。而生产者与消费者之间的关系为互斥与同步,互斥就是竞争关系,因为邻界资源的访问一次只能允许一个执行分支访问,所以:如果我在写(生产),你就不能读(消费);同步关系表示为:我在写完之后,就会调用函数给你信号,唤醒你来消费,解决二者之间可能出现的饥饿问题。
  

生产者–消费者间的关系

  所以生产者与消费者之间的关系可以总结如下几点:
  生产者与生产者:互斥关系;
  消费者与消费者:互斥关系;
  生产者与消费者:互斥与同步关系。

生产者与消费者模型的实现

  实现生产者与消费者模型时我们需要用到互斥锁,与条件变量。

互斥锁

   互斥锁即为保护邻界资源的一个保护“装置”,这个我之前解释过,跟二元信号量有类似的地方。当邻界资源未有执行分支使用时,锁是开着的,只要一有执行分支取访问邻界资源,那么锁就会上锁,向外表明:这块邻界资源已经有“人”使用,别“人”来之只能等待(可以使阻塞和非阻塞),当访问邻界资源的执行分支操作完毕,告诉互斥锁,我已经访问完毕,互斥锁开锁,向外界表明:此时邻界资源无“人”使用,可以申请。互斥锁还会保证高优先级的执行分支不能一直持有锁,这样理解:每当一个执行分支操作完毕,就将他的优先级降低,让他去等待队列的后面排队。
  
   相关函数:
  

       #include <pthread.h>pthread_t lock;//定义一个锁,如果这个锁是全局的,就使用宏来初始化,如果是局部的,就使用函数pthread_mutex_init来初始化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 <pthread.h>int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);//计时等待方式如果在给定时刻前条件没有满足,则返回ETIMEDOUT,结束等待//即当前条件不满足我的访问条件,所以是当前执行分支挂起等待int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);//同上,没有计时,直到满足条件为止int pthread_cond_broadcast(pthread_cond_t *cond);//唤醒所有的条件变量导致进入挂起等待的执行分支int pthread_cond_signal(pthread_cond_t *cond);//唤醒单个

基于单链表的生产者–消费者模型

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>typedef struct list{struct list* next;int data;
}Node,*PNode;Node* head;//定义一个全局的单链表,是两个的两个线程公共资源
pthread_cond_t procon=PTHREAD_COND_INITIALIZER;//定义条件变量并初始化
pthread_mutex_t lock=PTHREAD_MUTEX_INITIALIZER;//定义互斥锁并初始化void initlist(PNode* head)
{*head=NULL;
}void* consumer(void* val)
{int i=1;while(i){Node* tmp=NULL;pthread_mutex_lock(&lock);//上锁,保证访问安全while(NULL == head)//用while原因是pthread_cond_wait可能调用失败{printf("list empty,consunistd.humer just waitting...\n");pthread_cond_wait(&procon,&lock);//如果此时链表为空,即无法访问,所以consumer挂起等待product函数插入节点后访问,//当product函数插入节点时,会唤醒consumer}tmp=head;head=tmp->next;tmp->next=NULL;printf("----------------pthread tid: %u\n",pthread_self());//当前线程的tidprintf("consumer success, total: %d\n",i++);pthread_mutex_unlock(&lock);//解锁free(tmp);tmp=NULL;}return NULL;
}void* product(void* val)
{initlist(&head);int i=1;while(i){pthread_mutex_lock(&lock);Node* node=(Node*)malloc(sizeof(Node));node->data=i;node->next=head;head=node;printf("product success,total: %d\n",i++);pthread_mutex_unlock(&lock);sleep(1);pthread_cond_signal(&procon);//取唤醒consumer}return NULL;
}
int main()
{void *ret;pthread_t pro,con,con1;pthread_create(&pro,NULL,product,NULL);pthread_create(&con,NULL,consumer,NULL);pthread_create(&con1,NULL,consumer,NULL);pthread_join(pro,&ret);pthread_join(con,&ret);return 0;
}

程序结果截图:

注意事项:

  提醒一点,create线程后必须join线程,不然线程不会运行,别问我为什么。我去哭会/(ㄒoㄒ)/~

基于环形队列的多线程的生产者–消费者模型

相关函数:

  初始化函数,*sem表示信号量,pshared为0时表示可供同一进程中的多个线程同步,value为信号量的数量,即邻界资源的数量

       #include <semaphore.h>int sem_init(sem_t *sem, int pshared, unsigned int value);

  sem_wait函数相当于二元信号量的P操作,给当前信号量表示的邻界资源的数量减一

       #include <semaphore.h>int sem_wait(sem_t *sem);int sem_trywait(sem_t *sem);int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

  sem_post函数相当于二元信号量的V操作,给当前信号量表示的邻界资源的数量加一

       #include <semaphore.h>int sem_post(sem_t *sem);

  sem_destroy函数,销毁*sem表示的信号量。

       #include <semaphore.h>int sem_destroy(sem_t *sem);
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <semaphore.h>
#define SIZE 10sem_t product_sem;
sem_t consumer_sem;
static int pro=SIZE;//一开始生产者可以生产
static int con=0;//一开始没有产品可供消费
static int p=0;//p,c,d变量均是为了打印定义的
static int c=0;
static int d=0;
static int table[SIZE];//用数组表示一个环形队列void* product_fun(void* val)
{int i=1;int index=0;while(i){sem_wait(&product_sem);//申请一个可生产的空间,即申请成功表示邻界资源可写,否则表示邻界资源已满不可写入。table[p]=i;pro--;con++;printf("-----------------------------------------\n");printf("which product:%u\n",pthread_self());//哪个生产者生产的printf("product#  product success,total:%d\n",i++);//一共生产了多少个printf("profuct#  you have %d blank source!\n",pro);//生产者可用的生产空间printf("product#  you have %d data source!\n",con);//消费可供消费的产品的数量for(index=0;index<SIZE;index++)//输出当前生产的所有产品{if(table[index]!=0)printf("array[%d]:%d ",index,table[index]);}printf("\n");printf("-----------------------------------------\n");printf("\n");sem_post(&consumer_sem);//给消费者可供消费的产品数量加一,即将邻界资源中的可使用的数据加一p=(p+1)%SIZE;//下标调整sleep(1);}return NULL;
}void* consumer_fun(void* val)
{int i=1;int temp=0;while(i){sem_wait(&consumer_sem);// 申请一个消费者可消费的产品,即此时邻界资源中有可使用的数据。if(c!=0)d=c-1;temp=table[c];table[c]=0;pro++;con--;printf("##########################################\n");printf("which consumer:%u\n",pthread_self());//哪个消费者消费printf("consume: %d\n",temp);//消费的数据为tempprintf("consumer#  you have %d data source!\n",con);//可供消费的数据量printf("consumer#  you have %d blank source!\n",pro);//可供生产的空间printf("##########################################\n");printf("\n");sem_post(&product_sem);//给邻界资源可写的资源加一,表示消费一个数据,拿走数据后,空出一个位置可写c=(c+1)%SIZE;}
}void destroy()
{sem_destroy(&product_sem);sem_destroy(&consumer_sem);exit(0);
}void initsem()
{signal(2,destroy);//信号捕捉函数,自定义2号信号量为销毁两个信号量int i=0;for(i=0;i<SIZE;++i)table[i]=0;sem_init(&product_sem,0,SIZE);sem_init(&consumer_sem,0,0);
}int main()
{initsem();pthread_t product1,product2,consumer1,consumer2;pthread_create(&product1,NULL,product_fun,NULL);pthread_create(&product2,NULL,product_fun,NULL);pthread_create(&consumer1,NULL,consumer_fun,NULL);pthread_create(&consumer2,NULL,consumer_fun,NULL);pthread_join(product1,NULL);pthread_join(product2,NULL);pthread_join(consumer1,NULL);pthread_join(consumer2,NULL);return 0;
}

程序结果截图:




如有错误,请指出!

Linux基于单链表环形队列的多线程生产者消费者模型相关推荐

  1. python多线程实现生产者消费者_用Python实现多线程“生产者-消费者”模型的简单例子...

    用 Python 实现多线程"生产者 - 消费者"模型的简单例子 生产者消费者问题是一个著名的线程同步问题, 该问题描述如下: 有一个生产者在生产产品, 这些产品将提供给若干个消费 ...

  2. Linux多线程——生产者消费者模型

    目录 一.生产者消费者模型 1.1 什么是生成者消费者模型 1.2 生产者消费者模型的优点 1.3 基于阻塞队列实现生产者消费者模型 1.4 POSIX信号量 1.4.1 信号量概念 1.4.2 P操 ...

  3. 进程同步控制(锁,信号量,事件), 进程通讯(队列和管道,生产者消费者模型) 数据共享(进程池和mutiprocess.Pool模块)...

    参考博客 https://www.cnblogs.com/xiao987334176/p/9025072.html#autoid-1-1-0 进程同步(multiprocess.Lock.Semaph ...

  4. 多线程-生产者-消费者模型

    一.前言 生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例.该问题描 ...

  5. 多线程生产者消费者模型

    1. 基础知识: 1. 什么是生产者-消费者模式: 比如有两个进程A和B,它们共享一个固定大小的缓冲区,A进程产生数据放入缓冲区,B进程从缓冲区中取出数据进行计算,那么这里其实就是一个生产者和消费者的 ...

  6. 什么是阻塞队列?阻塞队列的实现原理是什么?如何使用阻塞队列来实现生产者-消费者模型?

    阻塞队列(BlockingQueue)是一个支持两个附加操作的队列. 这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空.当队列满时,存储元素的线程会等待队列可用. 阻塞队列常用于生产 ...

  7. 多线程——生产者消费者模型

    写一个固定容量同步容器,拥有put和get方法,以及getCount方法,能够支持2个生产者线程以及10个消费者线程的阻塞调用. 1.synchronized同步方法 public class Con ...

  8. 队列、生产者消费者模型

    目录 队列.生产者消费者模型.初识线程 一.用进程锁来优化抢票小程序 1.1 进程锁 1.2 优化抢票小程序 二.队列 2.1 队列的介绍 2.2 创建队列的类 2.3 使用队列的案例 三.生产者消费 ...

  9. [Linux]生产者消费者模型(基于BlockQueue的生产者消费者模型 | 基于环形队列的生产者消费者模型 | 信号量 )

    文章目录 生产者消费者模型 函数调用角度理解生产者消费者模型 生活角度理解生产者消费者模型 为什么要使用生产者消费者模型 生产者消费者模型优点 321原则 基于BlockingQueue的生产者消费者 ...

最新文章

  1. java 给窗口加菜单_程序求助:如何给窗口添加菜单?
  2. (Java多线程)线程安全问题
  3. 阴差阳错2019-12-13
  4. Linux文件预读对系统的影响
  5. 关于Kafka分区与分段的几个问题
  6. Flask中数据库的应用
  7. 实战Linux Bluetooth编程 (八) Class of Device
  8. 关于Decision in process状态时间变化的解释
  9. 学生DW静态网页设计 红色中国文化主题网站设计 ——美丽中国1页HTML+CSS
  10. 解决Windows Server 2008 System进程占用80端口问题
  11. [bzoj4199][后缀数组][后缀自动机]品酒大会
  12. Metasploit利用vnc图形化远程控制工具获得靶机远程控制桌面
  13. Aras Innovator: Catagoy, Itemtype, Item, Relationship的视图
  14. Visio方向键无法移动对象的解决办法[笔记本版]
  15. 最全面的Fiddler界面讲解#工作原理#菜单栏#工具栏#底部状态栏#底部自带命令行控制台#session栏#request栏和response栏
  16. 使用夜神模拟器进行Android开发调试
  17. 使用python爬取抖音app视频(appium可以操控手机)
  18. 机原自检——第3章 平面机构的运动分析
  19. Android面试:整理了Android面试官最常问的174道面试题,让你秒变offer收割机
  20. 总计超5万星!GitHub上10个超级好玩的项目

热门文章

  1. ARMV4,ARMV4T,ARMV4I, ARMv5te,armv6,ARM VFP,ARM neon
  2. python 不等于None 不等于空_高考励志标语条幅押韵:不拼不搏等于白活
  3. 2020年江苏中职学测计算机基础考什么,2020年新版中职计算机基础期末试卷及答案.docx...
  4. 51job互联网广告投放与中国网站排名分析
  5. 【U8+】科迈与用友U8合作的远程接入产品相关问题
  6. elementui 引入 iconfont 图标
  7. ICPC2017 Naning - The Maximum Unreachable Node Set
  8. 2021WSB-day3-3:Yasushi教授介绍步态识别的最新研究进展
  9. K-ROSET版本更新功能与授权验证发展史
  10. 官网下载Apache