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

简介: C语言生产者与消费者问题

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() 在更改数据之前等待空闲空间或可用项。

具体题解代码如下:

#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

…………

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

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

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

如果大家觉得有用的话,可以关注我下面的微信公众号,极客李华,我会在里面更新更多行业资讯,企业面试内容,编程资源,如何写出可以让大厂面试官眼前一亮的简历等内容,让大家更好学习编程,我的抖音,B站也叫极客李华。大家喜欢也可以关注一下

相关文章
|
2月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
39 3
|
18天前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
39 10
|
18天前
|
存储 程序员 C语言
【C语言】文件操作函数详解
C语言提供了一组标准库函数来处理文件操作,这些函数定义在 `<stdio.h>` 头文件中。文件操作包括文件的打开、读写、关闭以及文件属性的查询等。以下是常用文件操作函数的详细讲解,包括函数原型、参数说明、返回值说明、示例代码和表格汇总。
41 9
|
18天前
|
存储 Unix Serverless
【C语言】常用函数汇总表
本文总结了C语言中常用的函数,涵盖输入/输出、字符串操作、内存管理、数学运算、时间处理、文件操作及布尔类型等多个方面。每类函数均以表格形式列出其功能和使用示例,便于快速查阅和学习。通过综合示例代码,展示了这些函数的实际应用,帮助读者更好地理解和掌握C语言的基本功能和标准库函数的使用方法。感谢阅读,希望对你有所帮助!
31 8
|
18天前
|
C语言 开发者
【C语言】数学函数详解
在C语言中,数学函数是由标准库 `math.h` 提供的。使用这些函数时,需要包含 `#include <math.h>` 头文件。以下是一些常用的数学函数的详细讲解,包括函数原型、参数说明、返回值说明以及示例代码和表格汇总。
40 6
|
18天前
|
存储 C语言
【C语言】输入/输出函数详解
在C语言中,输入/输出操作是通过标准库函数来实现的。这些函数分为两类:标准输入输出函数和文件输入输出函数。
103 6
|
18天前
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
48 6
|
18天前
|
C语言 开发者
【C语言】断言函数 -《深入解析C语言调试利器 !》
断言(assert)是一种调试工具,用于在程序运行时检查某些条件是否成立。如果条件不成立,断言会触发错误,并通常会终止程序的执行。断言有助于在开发和测试阶段捕捉逻辑错误。
27 5
|
29天前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
51 4