在今年面试的时候,被问到了关于线程和进程的问题,结果当时还没学到这一块,所以理解很不透彻,最后只能和面试官承认自己这一块没咋好好了解。最近正好操作系统把生产者消费者问题讲完了,所以今天就来记录一下我写的生产者消费者问题的C语言版本。
生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#define true 1
#define producerNum 3 // 有3个生产者
#define consumerNum 5 // 有5个消费者
#define sleepTime 1 // 等待时间为1000ms
#define N 3 // 缓冲区容量
int buffer[N] = {0}; // 缓冲区
int in = 0; // 入队点
int out = 0; // 出队点
int proCount = 0; // 产品计数器
int value;
sem_t mutex, // 缓冲区是否被占用的标志
empty, // 缓冲区剩余可放产品数量
full, // 缓冲区目前存在产品数量
proCmutex; // 是否同时进行生产的标志
// 生产者实例
void * producer(void * a){
while (true) {
sem_wait(&proCmutex); // 如果正在进行其他生产则一直等待
proCount++; // 生产一个产品后产品数加1
sem_post(&proCmutex); // 结束生产
sem_wait(&empty); // 如果缓冲区已满,等待消费者消费产品
sem_getvalue(&empty, &value);
if (value < 0)
printf("empty有问题, 为%d!\n", value);
sem_wait(&mutex);
buffer[in] = proCount; // 将产品存入缓冲区
in = (in + 1) % N; // 指向下一个存放产品的缓冲位置
printf("生产一个产品, ID为%d, 缓冲区位置为%d\n", proCount, in);
sem_post(&mutex); // 释放缓冲区
sem_post(&full);
sem_getvalue(&empty, &value);
if (value < 0)
printf("empty有问题, 为%d!\n", value);
sleep(sleepTime); // 等待1s
}
}
// 消费者实例
void * consumer(void *b) {
while(true) {
sem_wait(&full); // 缓冲区是否为空,如果为空,等待生产者生产
sem_getvalue(&full, &value);
if (value < 0) {
printf("full有问题, 为%d!\n", value);
}
sem_wait(&mutex); // 如果缓冲区被占用,则一直等待
int nextc = buffer[out]; // 取出需要消费的产品
buffer[out] = 0; //消费完将缓冲区设置为0
out = (out + 1) % N; // 移向下一次消费的产品在缓冲区中的位置
printf("消费一个产品, ID为%d, 缓冲区位置为%d\n", nextc, out);
sem_post(&mutex); // 释放缓冲区
sem_getvalue(&empty, &value);
if (value < 0)
printf("empty有问题, 为%d!\n", value);
sem_post(&empty);
sleep(sleepTime); // 等待1s
}
}
int main()
{
// 创建一个线程池,存放所有生产者和消费者实例所需要的占用的线程
pthread_t threadPool[producerNum + consumerNum];
int i;
if (sem_init(&mutex, 0, 1) == -1)
exit(1);
if (sem_init(&empty, 0, N) == -1)
exit(1);
if (sem_init(&full, 0, 0) == -1)
exit(1);
if (sem_init(&proCmutex, 0, 1) == -1)
exit(1);
for (i = 0; i < producerNum; i++) { // 创建生产者线程放入线程池
pthread_t temp; // 创建生产者线程
if (pthread_create(&temp, NULL, producer, NULL) == -1) {
printf("错误, 创建生产者%d号失败\n", i);
exit(1);
}
threadPool[i] = temp; // 在线程池中逐个存放生产者线程
}
for (i = 0; i < consumerNum; i++) { // 创建消费者线程放入线程池
pthread_t temp; // 创建消费者线程
if (pthread_create(&temp, NULL, consumer, NULL) == -1) {
printf("错误, 创建消费者%d号失败\n", i);
exit(1);
}
threadPool[i+producerNum] = temp; // 在线程池中逐个存放消费者线程
}
void * result; // 运行线程池
for (i = 0; i < producerNum + consumerNum; i++) {
if (pthread_join(threadPool[i], &result) == -1) {
printf("fail to recollect\n"); // 逐个开启线程
exit(1);
}
}
return 0;
}
网上关于Java和Python的版本已经有很多了,但是关于C语言的并不是特别多。其中碰到了一些很奇怪的问题,就是在Windows环境下运行时(做出一些修改),会出现打印顺序完全错乱的情况
而在Linux环境下运行则非常正确,运行时很有序。老师在指导我的时候认为是因为我在代码中采用的int变量导致出现的问题,因为int变量在编译为汇编语言之后,会出现汇编语言的语句较多,容易在运行中途被打断的情况,所以会导致这样的情况出现。
如下为在Linux上运行的结果
[