生产者消费者模型

什么是生产者消费者模型

  在实际的开发中,经常会碰到如下场景:某个模块负责生产数据,某个模块负责处理这些数据。产生数据的模块就称为生产者,而处理数据的模块就称为消费者。这个模型还需要一个缓冲区来作为中介,生产者将产生的数据放入缓冲区中,消费者从缓冲区将数据取出并处理。
描述

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

  1. 解耦
    假设生产者和消费者是两个类,如果让生产者直接调用消费者的某个方法,那么生产者和消费者之间就产生依赖(耦合),此时不管是消费者还是生产者的代码发生改变,都会影响到对方。如果两者都依赖某个缓冲区而不直接依赖,耦合度降低。
  2. 支持并发
    生产者直接调用消费者的某个方法还有一个弊端。由于函数调用是同步的(或者称作为阻塞的),在消费者的方法没有返回之前,生产者只好一直等待,如果消费者处理数据很慢,则生产者就会浪费大量时间在等待上面。使用生产者消费者模型后,生产者和消费者可以是两个独立的并发主体。生产者将生产出来的数据放入缓冲区后就可以继续生产下一个数据,不再依赖消费者的处理速度。
  3. 支持忙闲不均
    如果生产数据的速度时快时慢,缓冲区的作用也就体现出来了,当生产数据很快,消费者还来不及处理,未处理的数据就会放入缓冲区中,等到生产者的速度慢下来后,消费者再慢慢处理。

生产者和消费者之间的关系

  • 生产者和生产者之间是互斥关系
  • 消费者和消费者之间是互斥关系
  • 生产者和消费者之间是同步、互斥关系

也就是说:

  • 一次只能有一个生产者生产,一个消费者消费。
  • 生产者生产的时候消费者不能消费。
  • 消费者消费的时候生产者不能生产。
  • 缓冲区满时生产者不能生产。
  • 缓冲区空时消费者不能消费。

生产者消费者模型实现方案

基于链表的生产者消费者模型,其空间可以动态分配。

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

pthread_mutex_t g_lock;
pthread_cond_t g_cond;

//先实现交易场所,使用一个链表来实现
//带头结点不带环的单向链表
//此时我们的链表就是一个栈
typedef struct Node{
    int data;
    struct Node* next;
}Node;

Node g_head;

Node*CreateNode(int value){
    Node* ptr = (Node*)malloc(sizeof(Node));
    ptr->data = value;
    ptr->next = NULL;
    return ptr;
}

void DestoryNode(Node* ptr){
    free(ptr);
}

void Init(Node* head){
    head->data = 0;
    head->next = NULL;
}

void Push(Node* head, int value){
    if(head == NULL){
        return;
    }
    Node* new_node = CreateNode(value);
    new_node->next = head->next;
    head->next = new_node;
    return;
}

void Pop(Node* head, int* value){
    if(head == NULL){
        return;
    }
    if(head->next == NULL){
        //链表为空
        return;
    }
    Node* to_delete = head->next;
    head->next = to_delete->next;
    *value = to_delete->data;
    DestoryNode(to_delete);
}

//再实现来那个两种角色
void* Product(void* arg){
    (void)arg;
    int count = 0;
    while(1){
        pthread_mutex_lock(&g_lock);
        Push(&g_head, ++count);
        printf("Product %d\n", count);
        pthread_mutex_unlock(&g_lock);
        usleep(123456);
        pthread_cond_signal(&g_cond);
    }
    return NULL;
}

void* Consume(void* arg){
    (void)arg;
    int count = -1;
    while(1){
        pthread_mutex_lock(&g_lock);
        //pthread_cond_wait 有可能被信号打断
        //当我们的pthread_cond_wait返回的时候,再次判断条件是否就绪
        while(g_head.next == NULL){
            pthread_cond_wait(&g_cond, &g_lock);
        }
        Pop(&g_head, &count);
        printf("Consume %d\n", count);
        usleep(789123);
        pthread_mutex_unlock(&g_lock);
    }
    return NULL;
}

int main(){
    pthread_cond_init(&g_cond, NULL);
    pthread_mutex_init(&g_lock, NULL);
    Init(&g_head);
    pthread_t product, consume;
    pthread_create(&product, NULL, Product, NULL);
    pthread_create(&consume, NULL, Consume, NULL);
    pthread_join(product, NULL);
    pthread_join(consume, NULL);
    pthread_mutex_destroy(&g_lock);
    pthread_cond_destroy(&g_cond);
    return 0;
}

基于固定大小的环形队列,利用信号量(POSIX版本的信号量)实现生产者消费者模型

初始化信号量:

#include<semaphore.h>
int sem_init(sem_t* sem,int pshared,unsigned int value);
//参数:
//pshared:0表示线程间共享,非零表示进程间共享
//value:信号量初始值

销毁信号量:

int sem_destroy(sem_t* sem);

等待信号量:

//功能:等待信号量,会将信号量的值减1
int sem_wait(sem_t* sem);

发布信号量:

//功能:发布信号量,表示资源使用完毕,可以归还资源了。将信号量值加1。
int sem_post(sem_t* sem);
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
////////////////////////////////////////////////////////////
//基于信号量实现生产者消费者模型
//POSIX版本的信号量
////////////////////////////////////////////////////////////
#include <semaphore.h>

sem_t g_lock;
sem_t g_data;
sem_t g_blank;

//先实现一个交易场所
//使用数组实现一个队列
#define SIZE 1024
typedef struct Queue{
    int array[SIZE];
    int head;
    int tail;
    int size;
}Queue;

Queue g_queue;

void Init(Queue* q){
    q->head = q->tail = q->size = 0;
}

void Push(Queue* q, int value){
    if(q->size >= SIZE){
        return;
    }
    q->array[q->tail++] = value;
    if(q->tail >= SIZE){
        q->tail = 0;
    }
    ++q->size;
}

void Pop(Queue* q, int * value){
    if(q->size == 0){
        return;
    }
    *value = q->array[q->head++];
    if(q->head >= SIZE){
        q->head = SIZE;
    }
    --q->size;
}

//再实现来那个两种角色
void* Product(void* arg){
    (void)arg;
    int count = 0;
    while(1){
        sem_wait(&g_blank);

        sem_wait(&g_lock);
        Push(&g_queue, ++count);
        printf("Product %d\n", count);
        sem_post(&g_lock);

        usleep(789123);
        sem_post(&g_data);
    }
    return NULL;
}

void* Consume(void* arg){
    (void)arg;
    int count = -1;
    while(1){
        sem_wait(&g_data);

        sem_wait(&g_lock);
        Pop(&g_queue, &count);
        printf("Consume %d\n", count);
        sem_post(&g_lock);

        usleep(123456);
        sem_post(&g_blank);
    }
    return NULL;
}

//再实现三种关系
int main(){
    Init(&g_queue);
    sem_init(&g_lock, 0, 1);
    sem_init(&g_data, 0, 0);
    sem_init(&g_blank, 0, SIZE);
    pthread_t product, consume;
    pthread_create(&product, NULL, Product, NULL);
    pthread_create(&consume, NULL, Consume, NULL);
    pthread_join(product, NULL);
    pthread_join(consume, NULL);
    sem_destroy(&g_lock);
    sem_destroy(&g_data);
    sem_destroy(&g_blank);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值