linux:线程的同步互斥之生产者&消费者模型

一、.什么是生产者消费者模型?

在实际开发过程中,经常会遇到如下场景:

某个模块负责生产数据,这些数据由另一个模块来负责处理,产生数据的模块可以形象的看为生产者,处理数据的模块可以形象的看为消费者,但是生产者生产的东西需要存放在一个地方从而来供消费者使用,存放的地方可以是一个仓库,作为中介,联系啊生产者和消费者

生产者把生产的数据存放到缓冲区中,消费者从缓冲区中取出数据,从而就实现了生产者消费者模型
这里写图片描述

二、为什么需要生产者消费者模型

1.解耦(耦合就是依赖)

生产者和消费者之间的联系依赖于一个缓冲区,并没有直接联系,假设没有生产者消费者模型,生产者和消费者之间的联系靠生产者调用消费者的某个函数来实现,那么生产者和消费者之间就会产生依赖,如果消费者的代码改变了那就会影响到生产者耦合性(依赖性)就降低了。

2.支持并发

生产者直接调用消费者的某个方法,还有一个弊端,由于函数的调用时同步的(或者称做阻塞),那就会出现在消费者没有调用完函数之前,生产者就只能等待,从而生产者的时间,使用生产者消费者模型,生产者生产了数据放到缓冲区就好,就可以生产下一个数据,不需要管消费者什么时候取走数据,因为生产者和消费者是两个独立的主体

3.支持忙闲不均

缓冲区还有一个好处就是如果生产者生产的快了,消费者来不及处理的话,数据可以放到缓冲区中,生产者如果生产的慢了,缓冲区还有数据可以供消费者使用

三、生产者消费者模型的特点

1.一个交易场所

交易场所(存放数据的地方):可以是一个变量、一个数组、一个链表或者数据结构等

2.两种角色:

   (1)生产者将数据放到交易场所中;
   (2)消费者从交易场所中取数据;

3.三种关系:

(1)生产者与生产者之间是互斥关系;
(2)消费者与消费者之间是互斥关系;
(3)生产者与消费者之间是同步互斥关系;

ps:生产者生产的时候消费者不能消费
   消费者消费的时候生产者不能生产  
   缓冲区空的话消费者不能消费   
   缓冲区满的化生产者不能生产

四、实现生产者消费者模型

使用链表作为交易场所,生产者生产一个结点头插到链表中,消费者在从链表中把插入的结点消费(删除),即就是头插的插入结点,头删的删除结点

consumers_producers.h

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>

#define PRODUCERS_COUNT 3//生产者的数量
#define CONSUMERS_COUNT 2//消费者的数量

pthread_mutex_t mutex;//声明互斥量
pthread_cond_t cond;//声明条件变量
pthread_t thread[CONSUMERS_COUNT+PRODUCERS_COUNT];

//封装链表中的结点
struct msg
{
    int data;
    struct msg*next;
};


//生产者生产
void*producer(void*arg);
//消费者消费
void*consumer(void*arg);

consumers_producers.c

#include"consumers_producers.h"
struct msg*head=NULL;
//生产者生产
void*producer(void*arg)
{
    int num=*(int*)arg;
    free(arg);
    while(1){
        printf("%d begin produce product......\n",num);
        //1.构造结点
        struct msg* m;
        m=(struct msg*)malloc(sizeof(struct msg));
        m->data=rand()%1000+1;//随机的产生一个数,范围是1-1000
        printf("%d:> produce %d\n",num,m->data);
        //2.给临界资源上锁
        pthread_mutex_lock(&mutex);
        //3.生产结点,就是给生产者消费者模型中的交易场所里存放数据,供消费者使用
        m->next=head;//头插
        head=m;//头插完成后,更新头结点
        printf("%d  end produce product......\n",num);
        //4.交易场所中已有数据,通知消费者可以消费了,此通知是一对一的通知
        pthread_cond_signal(&cond);
        //5.解锁
        pthread_mutex_unlock(&mutex);
        sleep(2);//随机的睡眠几秒,范围是5秒之内
    }
}
//消费者消费
void*consumer(void*arg)
{
    int num=*(int*)arg;
    free(arg);
    while(1){
        //1.给将要访问的临界资源上锁
        pthread_mutex_lock(&mutex);
        //2.交易场所中没有数据的话,那就一直循环的等待
        while(head==NULL){
            printf("%d waitting contion......\n",num);
            pthread_cond_wait(&cond,&mutex);
        }
        printf("%d end wait condition......\n",num);
        printf("%d begin consume product......\n",num);
        //3.消费交易场所中的数据
        struct msg* m;
        m=head;//标记头结点,以便于头删
        head=m->next;//更新头结点
        //4.解锁
        pthread_mutex_unlock(&mutex);
        printf("%d:> consume %d\n",num,m->data);
        free(m);//删除头结点
        printf("%d end consume product......\n",num);
        sleep(3);
    }
}

test.c

#include"consumers_producers.h"
int main()
{
    srand(time(0));
    int i=0;
    pthread_mutex_init(&mutex,NULL);//初始化互斥量
    pthread_cond_init(&cond,NULL);//初始化条件变量
    //创建生产者线程
    for(i=0;i<PRODUCERS_COUNT;i++)
    {
        int *p=(int*)malloc(sizeof(int));
        *p = i;
        pthread_create(&thread[i],NULL,producer,(void*)p);
    }
    //创建消费者线程
    for(i=0;i<CONSUMERS_COUNT;i++)
    {
        int* p=(int*)malloc(sizeof(int));
        *p = i;
        pthread_create(&thread[i+CONSUMERS_COUNT],NULL,consumer,(void*)p);
    }
    //等待线程
    for(i=0;i<PRODUCERS_COUNT+CONSUMERS_COUNT;i++)
        pthread_join(thread[i],NULL);
    pthread_mutex_destroy(&mutex);//销毁互斥量
    pthread_cond_destroy(&cond);//销毁条件变量
    return 0;
}

运行结果:
这里写图片描述

编译命令:

gcc test.c consumers_producers.c -pthread
ps:记得一定要加 -pthread

在编译时遇到collect2: ld returned 1 exit status问题可参考博客:
https://blog.csdn.net/dangzhangjing97/article/details/80169103

猜你喜欢

转载自blog.csdn.net/dangzhangjing97/article/details/80020932