基于C语言的消费者与生产者执行

一、生产者消费者问题概述

描述:俩个或者更多的线程共享同一个缓冲区,其中一个或多个线程作为“生产者”会不断地向缓冲区中添加数据,另一个或者多个线程作为“消费者”从缓冲区中取走数据。要注意以下几点:
1. 生产者和消费者必须互斥的使用缓冲区
2. 缓冲区空时,消费者不能读取数据
3. 缓冲区满时,生产者不能添加数据

在这里插入图片描述

生产者-消费者模式之间的三种关系:

模拟实现生产者-消费者模式之前,我们需要先捋清除这之间的关系:

  • 生产者-生产者:很明显,这两者之间必定是一种竞争关系,也就是说一个生产者往缓冲区放数据时另一个生产者就不能去访问这块空间
  • 消费者-消费者:同样,两个消费者之间也是竞争的关系,这就好比两个人同时看中一件商品时,他们之间就是一种竞争的关系
  • 生产者-消费者:生产者与消费者之间其实是一种同步与互斥的关系,假设只有一个生产者一个消费者时,只有生产者放入数据后消费者才能读取,消费者拿到数据后生产者才去生产,这就是一种同步;但当生产者生产数据的时候消费者就不能从缓冲区拿数据,或者消费者读数据的时候生产者就不能往缓冲区里写数据,否则很可能会导致两者都存/取数据失败,产生二义性问题。

代码举例:

/**********************************************************************************************************************
 *  FILE DESCRIPTION
 *  -------------------------------------------------------------------------------------------------------------------
 *  文件编码格式为gb2312,cp936  
 *  $Id$ $DateTime$
 *  @file ProConer.c
 *  @brief 生产者与消费者模板
 *  @version 0.0.1
 *  @since 0.0.1
 *  @author melvyn<chris.guyue@gmail.com>
 *  @date 2022-07-04    create
 *
 *  \details  1-创建五个生产者、每个生产者占用一个线程,生产者每2秒生产一个产品。五个生产者线程在某个时间里用很短的时间
 *              乱序的将产品放进仓库里。生产者进入仓库的顺序是不确定的,与 ID 无关。
 * 
 *            2-创建五个消费者,每个消费者占用一个线程,消费者每5秒消费一个产品。五个消费者线程在某个时间里用很短的时间
 *              乱序的将产品取出仓库里。消费者进入仓库的顺序是不确定的,与 ID 无关。
 * 
 *            3-生产者与消费者不会出现搬运产品相撞的情况,生产者左近左出,消费者右进右出,不是同一个门。之所以显示会出问题,
 *              是因为两个线程进行同时打印,在terminal打印出了bug。
 * 
 *            4-在达到一定时间后,因为生产者生产产品的速度大于消费者取出产品的速度,仓库一定会被挤满。
 *              当仓库被挤满时,已经生产完产品的生产者线程会被阻塞并被挂起,生产线程暂停,未生产完产品的生产者继续进行生产,
 *              生产完产品后,也进入阻塞等待的环节,直到消费者开始消费。
 *              当消费者从仓库取出产品时,被挂起的某一个生产者线程就会进入仓库,放进一个产品,之后回到线程继续生产,其他生
 *              产者继续按照队列进行等待。
 *                
 *********************************************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sched.h>
#include <stdbool.h>
#include <string.h>

#define NumofProducer    5   
#define NumofConsumer    5   
#define SleepofProducer  2  
#define SleepofConsumer  5   
#define NumofStorage     50 

/* 生产者放置产品的位置 */
int InStockSite = 0;  
/* 消费者取产品的位置 */  
int OutofStockSite = 0; 
/* 缓冲初始化为0,开始时没有产品 */
int buff[NumofStorage] = { 0 }; 
/* 定义一个信号量,当仓库满时阻止生产者放产品,当没产品时阻止消费者消费 */
sem_t fullStock; 
sem_t emptyStock;  
/* 定义要用到互斥锁的线程,一次只有一个线程访问缓冲 */
pthread_mutex_t mutex_Producer;   
pthread_mutex_t mutex_Consumer;  
/* 生产者ID与消费者ID */
int ProducerID = 0; 
int ConsumerID = 0; 

//信号处理函数
void Handlesignal(int signo){
    printf("程序退出 %d\n",signo);
    exit(0);
}

/* 打印缓冲情况 */
void print() {
    int i;
    printf("产品队列为");
    char temp[NumofStorage+1];
    for(i = 0; i < NumofStorage; i++)
        if(buff[i]){
            temp[i]='*';
        }else{
            temp[i]='.';
        }
    temp[i]='\0';
    printf("%s\n",temp);
    fflush(stdout);
}

/**
 * @fn     product
 * @brief  生产者方法
 * @param  无 
 * @return 
 */
void *product() {
    int id = ++ProducerID;
    while(1) {
        sleep(SleepofProducer); /* 生产者每2秒生产一个产品 */
        sem_wait(&fullStock);   /* 当仓库满了时,阻止生产者放产品,线程处于阻塞状态 */
        pthread_mutex_lock(&mutex_Producer); /* 互斥锁,一次只有一个线程访问缓冲 */

        InStockSite= InStockSite % NumofStorage;
        printf(" + 生产者%d在产品队列中放入第%d个产品\t",id, InStockSite);
        fflush(stdout); /* 在printf()后使用fflush(stdout)的作用是立刻将要输出的内容输出 */

        buff[InStockSite]= 1;/* 仓库中的位置有产品时,仓库的地址位1 */
        print();
        ++InStockSite;

        pthread_mutex_unlock(&mutex_Producer);
        sem_post(&emptyStock); /* 当仓库空了时执行此函数,让生产者继续工作 */
    }
}

/**
 * @fn     prochase
 * @brief  消费者方法
 * @param  无 
 * @return 
 */
void *prochase() {
    int id = ++ConsumerID;
    while(1) {
        sleep(SleepofConsumer);/* 消费者每2秒生产一个产品 */
        sem_wait(&emptyStock); /* 当空了时执行此函数,阻止消费者取产品 阻塞等待 */
        pthread_mutex_lock(&mutex_Consumer);

        OutofStockSite= OutofStockSite % NumofStorage;
        printf(" - 消费者%d从产品队列中取出第%d个产品\t",id, OutofStockSite);
        fflush(stdout);

        buff[OutofStockSite]= 0;/* 仓库中的位置没有产品时,仓库的地址位0 */
        print();
        ++OutofStockSite;

        pthread_mutex_unlock(&mutex_Consumer);
        sem_post(&fullStock);/* 当仓库满了时执行此函数,让消费者继续工作 */
    }
}


int main() {
    printf("生产者%d个和消费者%d个,"
            "产品缓冲为%d,生产者每%d秒生产一个产品,"
            "消费者每%d秒消费一个产品,Ctrl+退出程序\n",
             NumofProducer,NumofConsumer,NumofStorage,SleepofProducer,SleepofConsumer);
    pthread_t id1[NumofConsumer];
    pthread_t id2[NumofProducer];
    int i;
    int ret[NumofConsumer+NumofProducer];
    //结束程序
    if(signal(SIGINT,Handlesignal)==SIG_ERR){/* 按ctrl+C产生SIGINT信号 */
        printf("信号安装出错\n");
    }

//TODO: 初始化同步信号量
    /* ret = sem_init(&sem, pshared, value); 
     * 如果 pshared 的值为零,则不能在进程之间共享信号。如果 pshared 的值不为零,则可以在进程之间共享信号。
     **/
    int ini1 = sem_init(&fullStock, 0, NumofStorage);//产品队列缓冲同步
    int ini2 = sem_init(&emptyStock, 0, 0);//线程运行同步
    if(ini1 && ini2 != 0) {
        printf("信号量初始化失败!\n");
        exit(1);
    }

//TODO: 初始化互斥信号量
    int ini3 = pthread_mutex_init(&mutex_Producer, NULL);
    int ini4 = pthread_mutex_init(&mutex_Consumer, NULL);
    if(ini3 != 0 ||ini4!=0) {
        printf("线程同步初始化失败!\n");
        exit(1);
    }

//TODO: 创建N个生产者线程
    for(i = 0; i < NumofProducer; i++) {
        /* 函数:pthread_create(pthread_t *th, const pthread_attr_t *attr, void *(* func)(void *), void *arg);
         * 第一个参数,用来保存创建好后线程 uid
         * 第二个参数,即线程属性,通常传 NULL,表示默认属性,这个属性在创建前可以设置,包括调度策略,栈大小,优先级等等
         * 第三个参数,即线程入口函数
         * 第四个参数,传给线程的参数
         */
        ret[i]= pthread_create(&id1[i], NULL, product, (void *) (&i));
        if(ret[i] != 0) {
            printf("生产者%d线程创建失败\n", i);
            exit(1);
        }

    }

//TODO: 创建N个消费者线程
    for(i = 0; i < NumofConsumer; i++) {
        ret[i]= pthread_create(&id2[i], NULL, prochase, NULL);
        if(ret[i] != 0) {
            printf("消费者%d线程创建失败\n", i);
            exit(1);
        }
    }

//TODO: 等待线程销毁
    for(i = 0; i < NumofProducer; i++) {
        pthread_join(id1[i], NULL);
        pthread_join(id2[i],NULL);
    }

    exit(0);
}
### 回答1: 对于生产者消费者问题的C语言代码,可以使用线程和互斥锁来实现。 以下是一种可能的实现方式: ```c #include <pthread.h> #include <stdio.h> #include <stdlib.h> #define BUFFER_SIZE 10 int buffer[BUFFER_SIZE]; int count = 0; // 当前缓冲区中元素的数量 int in = 0; // 生产者插入元素的位置 int out = 0; // 消费者取出元素的位置 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t empty = PTHREAD_COND_INITIALIZER; pthread_cond_t full = PTHREAD_COND_INITIALIZER; void *producer(void *arg) { int i; for (i = 0; i < BUFFER_SIZE * 2; i++) { pthread_mutex_lock(&mutex); while (count == BUFFER_SIZE) { pthread_cond_wait(&empty, &mutex); } buffer[in] = i; in = (in + 1) % BUFFER_SIZE; count++; printf("生产者生产了 %d\n", i); pthread_cond_signal(&full); pthread_mutex_unlock(&mutex); } return NULL; } void *consumer(void *arg) { int i, value; while (1) { pthread_mutex_lock(&mutex); while (count == 0) { pthread_cond_wait(&full, &mutex); } value = buffer[out]; out = (out + 1) % BUFFER_SIZE; count--; printf("消费者消费了 %d\n", value); pthread_cond_signal(&empty); pthread_mutex_unlock(&mutex); } } int main(int argc, char *argv[]) { pthread_t prod, cons; pthread_create(&prod, NULL, producer, NULL); pthread_create(&cons, NULL, consumer, NULL); pthread_join(prod, NULL); pthread_join(cons, NULL); return 0; } ``` 在这个代码中,我们定义了一个大小为10的缓冲区 `buffer`,一个计数器 `count` 来记录缓冲区中元素的数量,以及两个索引 `in` 和 `out` 分别表示生产者插入元素的位置和消费者取出元素的位置。 我们使用了线程和互斥锁来控制并发访问缓冲区的问题。在 `producer` 函数中,我们首先使用 `pthread_mutex_lock` 函数获取互斥锁,然后使用 `while` 循环来等待缓冲区中有空闲位置。当缓冲区中有空闲位置时,我们将一个元素插入到缓冲区中,并且更新 `in`、`count` 等变量。然后,我们发送一个信号给等待在 `empty` 条件变量上的线程,表示缓冲区中有数据可供消费。最后,我们释放互斥锁。 在 `consumer` 函数中,我们也使用了互斥锁和条件变量来等待缓冲区中有数据可供消费。当缓冲区中有数据可供消费时,我们取出一个元素,并且更新 `out ### 回答2: 生产者消费者问题是经典的并发编程问题,描述了多个生产者和多个消费者共同访问一个有界缓冲区的情况。以下是一个使用C语言实现的简化版本代码: ```c #include <stdio.h> #include <pthread.h> #define BUFFER_SIZE 5 int buffer[BUFFER_SIZE]; int count = 0; int in = 0; int out = 0; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t not_full = PTHREAD_COND_INITIALIZER; pthread_cond_t not_empty = PTHREAD_COND_INITIALIZER; void *producer(void *arg) { int item; while (1) { item = produce_item(); // 产生一个数据项 pthread_mutex_lock(&mutex); while (count == BUFFER_SIZE) pthread_cond_wait(&not_full, &mutex); buffer[in] = item; in = (in + 1) % BUFFER_SIZE; count++; pthread_cond_signal(&not_empty); pthread_mutex_unlock(&mutex); } } void *consumer(void *arg) { int item; while (1) { pthread_mutex_lock(&mutex); while (count == 0) pthread_cond_wait(&not_empty, &mutex); item = buffer[out]; out = (out + 1) % BUFFER_SIZE; count--; pthread_cond_signal(&not_full); pthread_mutex_unlock(&mutex); consume_item(item); // 消费一个数据项 } } int main() { pthread_t producer_thread, consumer_thread; pthread_create(&producer_thread, NULL, producer, NULL); pthread_create(&consumer_thread, NULL, consumer, NULL); pthread_join(producer_thread, NULL); pthread_join(consumer_thread, NULL); return 0; } ``` 以上代码中,生产者线程通过`produce_item()`函数产生一个数据项,然后将数据项放入缓冲区,当缓冲区已满时会阻塞等待。消费者线程通过`consume_item()`函数从缓冲区取出一个数据项进行消费,当缓冲区为空时会阻塞等待。通过互斥锁和条件变量的配合,实现了生产者消费者的正确同步与互斥操作。 ### 回答3: 生产者消费者问题是一个经典的线程同步问题,在多线程环境下,生产者线程负责生产产品,消费者线程负责消费产品。为了保证线程的安全并避免不正确的操作,我们需要使用互斥锁(mutex lock)和条件变量(condition variable)来进行线程间的同步。 下面是一个用C语言编写的生产者消费者问题的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> #define BUFFER_SIZE 5 // 缓冲区大小 int buffer[BUFFER_SIZE]; // 缓冲区 int in = 0; // 缓冲区的写指针 int out = 0; // 缓冲区的读指针 pthread_mutex_t mutex; // 互斥锁 pthread_cond_t not_full; // 缓冲区不满的条件变量 pthread_cond_t not_empty; // 缓冲区不空的条件变量 void *producer(void *param) { while (1) { pthread_mutex_lock(&mutex); while ((in + 1) % BUFFER_SIZE == out) { // 缓冲区已满,等待缓冲区不满的条件变量 pthread_cond_wait(&not_full, &mutex); } int item = rand() % 100; // 生产一个随机数 buffer[in] = item; in = (in + 1) % BUFFER_SIZE; printf("Producer produced item: %d\n", item); pthread_cond_signal(&not_empty); // 发送缓冲区不空的条件变量信号 pthread_mutex_unlock(&mutex); } } void *consumer(void *param) { while (1) { pthread_mutex_lock(&mutex); while (in == out) { // 缓冲区为空,等待缓冲区不空的条件变量 pthread_cond_wait(&not_empty, &mutex); } int item = buffer[out]; out = (out + 1) % BUFFER_SIZE; printf("Consumer consumed item: %d\n", item); pthread_cond_signal(&not_full); // 发送缓冲区不满的条件变量信号 pthread_mutex_unlock(&mutex); } } int main() { pthread_t producer_thread, consumer_thread; // 初始化互斥锁和条件变量 pthread_mutex_init(&mutex, NULL); pthread_cond_init(&not_full, NULL); pthread_cond_init(&not_empty, NULL); // 创建生产者消费者线程 pthread_create(&producer_thread, NULL, producer, NULL); pthread_create(&consumer_thread, NULL, consumer, NULL); // 主线程等待线程结束 pthread_join(producer_thread, NULL); pthread_join(consumer_thread, NULL); // 销毁互斥锁和条件变量 pthread_mutex_destroy(&mutex); pthread_cond_destroy(&not_full); pthread_cond_destroy(&not_empty); return 0; } ``` 以上是一个基于生产者消费者问题的简单示例代码,它通过互斥锁和条件变量来实现线程间的同步和控制。它能够保证在多线程环境下,生产者消费者线程的正确执行和互不干扰。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梅尔文

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值