OS实验-消费者/生产者问题(含代码实现)
问题描述
有一群生产者进程(线程)在生产产品,并将这些产品提供给消费者进程(线程)去消费。为使生产者进程(线程)与消费者进程(线程)能并发执行,在两者之间设置了一个具有n个缓冲区的缓冲池:生产者进程(线程)将它所生产的产品放入一个缓冲区中;消费者进程(线程)可从一个缓冲区中取走产品去消费。
它们之间必须保持同步原则:不允许消费者进程(线程)到一个空缓冲区去取产品;也不允许生产者进程(线程)向一个已装满产品且尚未被取走的缓冲区中投放产品。
本篇博客以线程的方式来实现。
实现方式
用一个数组来表示上述的具有n个(0,1,…,n-1)缓冲区的缓冲池,设置两个指针:
指针in:指示下一个可投放产品的缓冲区,每当生产者进程生产并投放一个产品后,in指针加1,即in∶=(in+1)mod n;
指针out:指示下一个可从中获取产品的缓冲区,每当消费者进程取走一个产品后,out指针加1,即out∶=(out+1) mod n。
当**(in+1) mod n=out时表示缓冲池满**;而in=out则表示缓冲池空。
但是如果直接使用这种方式,在并发执行时,就会出现差错(资源的竞争),因此此处引入互斥量mutex来解决互斥问题。
需要引入的头文件
stdio.h
stdlib.h
time.h ——> 用于产生随机数
pthread.h ——> 包含线程的创建等函数
semaphore.h ——> 用于信号量的设定
需要用到的函数
函数声明 | 作用 |
---|---|
int pthread_create(pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg); | 用于创建一个线程 |
int pthread_join(pthread_t thread,void rval_ptr); | 父线程等待子线程终止 |
sem_init(sem_t *sem, int pshared, unsigned int value); | 初始化一个信号量 |
sem_post(sem_t *sem); | 信号量P操作,信号量值加1;若有线程阻塞于信号量sem,则调度器会唤醒对应阻塞队列中的某一个线程 |
sem_wait(sem_t *sem); | 信号量V操作,若信号量值小于0,则线程阻塞于信号量sem,直到sem大于0;否则信号量值减1 |
具体函数的使用请见代码实现部分:
代码实现
/**
************************************************************
************************************************************
* 作者: 曾彬芮
*
* 日期: 2021-4-5
*
* 版本: V1.0
*
* 说明: 消费者/生产者问题的实现
************************************************************
************************************************************
**/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <semaphore.h>
#define CRITICAL_SECTION_NUM 10
#define PRODUCER_NUM 3
#define CUSTOMER_NUM 4
int CriticalSection[CRITICAL_SECTION_NUM] = {0};
sem_t empty; sem_t full; sem_t mutex;
int in = 0;
int out = 0;
void *PutData();
void *GetData();
void *PutData(){
while(1){
sem_wait(&empty);
sem_wait(&mutex);
CriticalSection[in] = rand()%9 + 1; // 产生一个1-9的随机数放入临界区
printf("I am producer and I put %d in\n",CriticalSection[in]);
in = (in+1)%(CRITICAL_SECTION_NUM);
sem_post(&mutex);
sem_post(&full);
}
}
void *GetData()
{
while(1){
sem_wait(&full);
sem_wait(&mutex);
printf("I am customer and I take %d out\n",CriticalSection[out]);
CriticalSection[out] = 0;
out = (out+1)%(CRITICAL_SECTION_NUM);
sem_post(&mutex);
sem_post(&empty);
}
}
int main()
{
sem_init(&empty, 0, CRITICAL_SECTION_NUM); // 使用sem_init函数进行初始化
sem_init(&full, 0, 0);
sem_init(&mutex, 0, 1);
pthread_t PRO1, PRO2, PRO3; // 三个生产者
pthread_t CUS1, CUS2, CUS3, CUS4; // 四个消费者
srand((unsigned)time(NULL)); // 用于产生随机数
pthread_create(&PRO1, NULL, PutData, NULL); // 以下创建三个生产者(线程)与四个消费者(线程)
pthread_create(&PRO2, NULL, PutData, NULL);
pthread_create(&PRO3, NULL, PutData, NULL);
pthread_create(&CUS1, NULL, GetData, NULL);
pthread_create(&CUS2, NULL, GetData, NULL);
pthread_create(&CUS3, NULL, GetData, NULL);
pthread_create(&CUS4, NULL, GetData, NULL);
pthread_join(PRO1, NULL); // 父线程等待子线程终止
pthread_join(PRO2, NULL);
pthread_join(PRO3, NULL);
pthread_join(CUS1, NULL);
pthread_join(CUS2, NULL);
pthread_join(CUS3, NULL);
pthread_join(CUS4, NULL);
return 0;
}
代码完成后在Linux终端使用命令 gcc -o ### ###.c -lpthread编译,再输入 ./### 运行即可(###表示文件名)。
以上代码均为自己编写,并已在Linux虚拟机下成功编译运行,若仍有问题还劳烦各位读者朋友指正!
注:创建线程时需要在gcc最后加上“-lpthread”,否则会报错。
2021.4.6 于 电子科技大学(沙河校区)