在今年面试的时候,被问到了关于线程和进程的问题,结果当时还没学到这一块,所以理解很不透彻,最后只能和面试官承认自己这一块没咋好好了解。最近正好操作系统把生产者消费者问题讲完了,所以今天就来记录一下我写的生产者消费者问题的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++; // 生产一个产品后产品数加1sem_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; //消费完将缓冲区设置为0out = (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上运行的结果

[
