生产者消费者模型

生产者消费者模型

简单来说就是“321原则(只是为了理解生产者消费者模型所取的)”
‘3’代表的是三种关系
生产者与消费者的互斥与同步关系

生产者与生产者的互斥(或竞争)关系

消费者与消费者的互斥(或竞争)关系
‘2’代表两种角色
生产者:往交易场所放东西(在计算机中一般都是数据)的人

消费者:从交易场所取东西的人

‘1’代表一个交易场所(类似于一个仓库)
所谓交易场所就是内存中具有存储数据的一段有界缓冲区
下面用图来解释:
这里写图片描述
下面写第一种生产者消费者模型。
基于链表的生产者消费者模型
分析:使用链表来模拟生产者消费者模型时,我们借助链表的插入来扮演生产者的角色,用链表的删除来充当消费者的角色,为了便于实现,我们直接采用链表的头插和链表的头删操作来模拟放数据和取数据这两个过程。
下面为实现的代码:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<time.h>
typedef struct Node{
    struct Node*next;
    int data;
}node_t,*pNode,**ppNode;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;   //互斥锁
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//条件变量
pNode allocNode(int x)//创建新节点
{
    pNode n=(pNode)malloc(sizeof(node_t));
    if(n==NULL){
        perror("malloc");
        return NULL;
    }
    n->data=x;
    n->next=NULL;
    return n;
}
int isEmpty(pNode head)//链表的判空
{
    return head->next==NULL?1:0;
}
void initList(ppNode head)//链表的初始化
{
    *head=allocNode(0);
}
void listpush(pNode head,int x)//链表的插入
{
    pNode n=allocNode(x);
    n->next=head->next;
    head->next=n;
}
void listpop(pNode head,int*x)//链表的删除
{   
    if(!isEmpty(head)){
        pNode n=head->next;
        head->next=n->next;
        if(x!=NULL){
        *x=n->data;
        }
        free(n);
    }
}
void showList(pNode head)//链表的打印
{
    if(!isEmpty(head)){
        head=head->next;
        while(head){
            printf("%d ",head->data);
            head=head->next;
        }
        printf("\n");
    }
}
void destroyList(pNode head)//链表的销毁
{
    while(head){
        listpop(head,NULL);
    }
    free(head);
}
void *runp(void*arg)//生产者
{
    pNode hp=*((ppNode)arg);
    int data=0;
    while(1){
        data=rand()%100+1;
        pthread_mutex_lock(&lock);
        listpush(hp,data);//head push
        printf("producter data is :%d\n",data);
        pthread_mutex_unlock(&lock);
        pthread_cond_signal(&cond);
        sleep(5);
    }
}
void *runc(void*arg)//消费者
{   
    pNode hp=*((ppNode)arg);
    int data=0;
    while(1){
    pthread_mutex_lock(&lock);
        while(isEmpty(hp)){//
            printf("list is empty,no data to read!\n");
            pthread_cond_wait(&cond,&lock);//**链表为空时,进行等待,将锁释放,直到wait返回时,又将互斥量恢复成原样。**
        }
            listpop(hp,&data);
            printf("consumer data is :%d\n",data);
        pthread_mutex_unlock(&lock);
        pthread_cond_signal(&cond);
        sleep(1);
    }
}
int main()
{

    pNode head=NULL;
    initList(&head);
    srand((unsigned long)time(NULL));
    pthread_t c,p;
    pthread_create(&p,NULL,runp,(void*)&head);
    pthread_create(&c,NULL,runc,(void*)&head);
    pthread_join(p,NULL);
    pthread_join(c,NULL);
//  int i=0;
//  int x=0;
//  for(;i<10;i++){
//      listpush(head,i);
//      showList(head);
//  }
//  for(;i>5;i--){
//      listpop(head,&x);
//      showList(head);
//  }
    destroyList(head);
    return 0;
}

运行结果如下:
这里写图片描述
1.链表被写满时,生产者会等。
2.链表为空时,消费者会等。
3.一旦写入,读取条件成立,双方立即被唤醒,则需两个信号量互相通信。

基于多元信号量的生产者消费者(循环队列)
该模型需要借助POSIX信号量:可以戳线程同步与互斥。(互斥量与条件变量、Posix信号量)
下面就来详细说说这种模型的一些规则:

生产者优先:若消费者优先,刚开始没有生产出数据,消费者也会被挂起;
消费者永远不能追上消费者:试想一下如果消费者追上生产者或者超过消费者的时候,此时消费者消费的并不是生产者实际所生产出的数据,而属于随机的数据;
生产者不能将消费者套一圈:如果生产者允许将消费者包一圈的话,那就相当于生产者可以无限的生产,并不停的覆盖掉原来所产生的数据,那么如果原来生产出的数据中如果有的是消费者需要获取的数据,那么除了生产者在次生产出该数据外,消费者将再也不能得到所想要的数据;

下面用图解来说明:
这里写图片描述
下面先来实现单生产者、单消费者模型:

#include<stdio.h>
#include<pthread.h>
#include<time.h>
#include<semaphore.h>
int ring[32];
sem_t semBlank;
sem_t semData;
int i;//缓冲池写入指针  
int j;//缓冲池读取指针  
void *runp(void*arg)
{   
    while(1){
        sem_wait(&semBlank);
        ring[i]=rand()%1234+1;
        int data=ring[i];
        i++;
        i%=32;
        printf("product done,data is:%d\n",data);
        sem_post(&semData);
        //sleep(1);

    }
}

void *runc(void*arg)
{
//  int data=0;
//  int i=0;
    while(1){
        sem_wait(&semData);
        int data=ring[j];
        j++;
        j%=32;
        printf("consumer done,data is:%d\n",data);
        sem_post(&semBlank);
        sleep(1);
    }
}
int main()
{
    sem_init(&semBlank,0,32);
    sem_init(&semData,0,0);
    srand((unsigned long)time(NULL));
    pthread_t t1,t2;
    pthread_create(&t1,NULL,runc,NULL);
    pthread_create(&t2,NULL,runp,NULL);
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    sem_destroy(&semBlank);
    sem_destroy(&semData);
}

这时分为三种情况:
1.若生产者快,则生产者满了之后,再是一次生产一次消费。
这里写图片描述
2.若消费者快,则一开始便为一次生产一次消费。
这里写图片描述
3.若生产者消费者同步,不加任何约束则生产后再消费。
这里写图片描述
实现多生产者、多消费者模型

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<time.h>
#include<semaphore.h>
#define PRODUCER_NUM 5 //生产者数目  
#define CONSUMER_NUM 5 //消费者数目 
int ring[11];
sem_t semBlank;
sem_t semData;
int i;//缓冲池写入指针  
int j;//缓冲池读取指针 
pthread_mutex_t mutex;


void *runp(void*arg)
{
    while(1){
        sem_wait(&semBlank);
        pthread_mutex_lock(&mutex);
        ring[i]=rand()%1234+1;
        int data=ring[i];
        i++;
        i%=11;
        printf("%d product done,data is:%d\n",(int)arg,data);
        pthread_mutex_unlock(&mutex);
        sem_post(&semData);
        sleep(1);   
    }
}

void *runc(void*arg)
{
    while(1){
        sem_wait(&semData);
          pthread_mutex_lock(&mutex);
        int data=ring[j];
        j++;
        j%=11;
        printf("%d consumer done,data is:%d\n",(int)arg,data);
          pthread_mutex_unlock(&mutex);
        sem_post(&semBlank);
    }
}
int main()
{
    pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
        //pthread_mutex_init(&mutex, NULL);  
    pthread_t producer_id[PRODUCER_NUM];  
    pthread_t consumer_id[CONSUMER_NUM];
    sem_init(&semBlank,0,11);
    sem_init(&semData,0,0);
    srand((unsigned long)time(NULL));
    int i=0;
    for(;i<PRODUCER_NUM;i++){
        int ret=pthread_create(&producer_id[i],NULL,runp,(void*)i);
         if (ret != 0){  
             printf("producer_id error");           
             exit(0);
             }
        int ret1=pthread_create(&consumer_id[i],NULL,runc,(void*)i);
         if (ret1!= 0){  
             printf("consumer_id error");                   
              exit(0);
             }
        }
        int j=0;
         for(;j<PRODUCER_NUM;j++){  
        pthread_join(producer_id[j],NULL);  
        pthread_join(consumer_id[j],NULL);  
        }
    sem_destroy(&semBlank);
    sem_destroy(&semData);
    return 0;
} 

下面就以消费者快为例运行结果:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值