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站也叫极客李华。大家喜欢也可以关注一下