首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >C语言生产者与消费者问题

C语言生产者与消费者问题

作者头像
GeekLiHua
发布2025-01-21 16:40:17
发布2025-01-21 16:40:17
3480
举报
文章被收录于专栏:JavaJava

C语言生产者与消费者问题

简介

本文讲解如何使用C语言来解决生产者与消费者问题。

相关在线编辑网站:https://www.ideone.com/whPQYr

题目原型

编写一个简单的生产者-消费者(producer-consumer)模型,其中包含一个生产者进程和一个消费者进程以及一个共享的缓冲区(使用队列或环形缓冲区)。生产者会更新缓存数据,而消费者则会使用该内存,程序按照如下规则运行:

  • 生产者会不断往缓存中写入随机的8位数
  • 消费者会从缓冲区中读取数字,每次第一位是偶数的数字被打印到屏幕上。
  • 要求:保证在生产者没有向缓冲区提交任何内容时,消费者不会读取缓冲区中的任何数字。

思路讲解

PV操作(信号量机制)是一种常见的用于线程同步和互斥的解决方案。这里简要介绍如何使用 PV 操作来实现给定问题的生产者/消费者模型。

首先创建两个信号灯 empty 和 full。empty 表示空缓存区数量,full 表示有数据可用的缓存区数量。其中 empty 的初始值应为缓存队列的大小,而 full 的初始值则应为 0。

对于生产者和消费者:

  • 生产者需要生成随机数并将其添加到缓冲区内,并且在此之后将 full 计数器加一。
  • 当 full 大于 0 时,消费者将从缓冲区中读取数据,并在满足特定条件时输出该数据。消费者检索当前可用的缓冲区,如果满足条件,则从计数器中减去 1。

在每次添加或读取信息成功时,在操作完成后,都需要调用 sem_wait() 在更改数据之前等待空闲空间或可用项。

具体题解代码如下:

代码语言:javascript
复制
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <semaphore.h>

#define QUEUE_SIZE 10       // 定义缓存区大小

int buffer[QUEUE_SIZE];     // 缓存区数组
int in = 0, out = 0;        // 输入和输出指针
sem_t full, empty;          // 初始化计数信号灯 
pthread_mutex_t lock;       // 声明互斥锁

// 生产者线程函数
void *producer(void *arg){
    while (1) {
        
        int new_item = some_producing_function(); // 生成新产品的函数
        
        sem_wait(&empty);   // 降低 empty 的值,等待可用缓存空间
        
        pthread_mutex_lock(&lock); // 加锁
        
        buffer[in] = new_item;  // 写入缓存区
        in = (in + 1) % QUEUE_SIZE; // 更新输入队列指针

        printf("Produced %d\n", new_item);

        pthread_mutex_unlock(&lock); // 解锁
        sem_post(&full);             // 提高 full 的值,增加可消费产品数量 

        sleep(2);   // 随机等待一段时间, 然后再生产下一个数据
    }
}

// 消费者线程函数
void *consumer(void *arg){
    while (1) {      
        sem_wait(&full);  //减小 full 的值,等待有可消费的产品       
        pthread_mutex_lock(&lock); //加锁

        int consumed_item = buffer[out]; // 从缓存区读取
        out = (out+1) % QUEUE_SIZE; // 更新输出队列指针
        printf("Consumed %d\n", consumed_item);

        pthread_mutex_unlock(&lock);  //解锁
        sem_post(&empty);  //提高 empty 的值,增加可用空间

        sleep(3); // 等待一段时间再进行下一次消费操作
    }
}

int main(int argc, char const **argv) 
{
    sem_init(&empty, 0, QUEUE_SIZE);  //初始化 empty信号灯,用于追踪缓存区为空的使用情况。其初始值为缓存队列的大小
    sem_init(&full, 0, 0);    // full信号灯 跟踪有多少产品已经被生产
    pthread_mutex_init(&lock, NULL);  // 初始化互斥锁

    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);

    sem_destroy(&empty);  // 销毁两个信号灯
    sem_destroy(&full);
    pthread_mutex_destroy(&lock); //销毁 mutex 执行完成之后

    return 0;
}

在该程序中,使用 PV 机制和互斥量来控制对缓存区的并发访问,保证了两个线程能够安全地使用共享数据组成的缓冲区。具体实现包括:

  • 初始化信号灯,空和满,并创建互斥锁
  • 在生产者中执行生产任务时,加锁/释放和等待/增加信号灯值操作来控制计数器的值。
  • 同样,在消费者中,操作信号灯允许或阻止消费行为。
  • 加锁保护每次修改缓存区中输入和输出指针的互斥访问。

这些机制的正确应用确保了必要的同步,并使程序不会数据竞争。

运行结果分析: 由于这是一个生产者-消费者模型的程序,最终的运行结果是不确定的。在理想情况下,该程序将会无限循环地生产产品并消费产品,并且输出如下所示:

Produced 1 Consumed 1 Produced 2 Consumed 2 Produced 3 Consumed 3 …………

但实际运行过程中,可能会出现以下情况:

  • 缓冲区满了,生产者必须等待消费者从缓冲区中取出产品,以释放空间。
  • 缓冲区为空,消费者必须等待生产者产生新的产品。

因此,最终的结果将取决于各种线程之间的执行顺序和时间分配。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-01-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • C语言生产者与消费者问题
    • 简介:
    • 题目原型:
    • 思路讲解:
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档