前言
实验题目:进程同步控制算法
【实验任务】
生产者-消费者问题(计算机系统中的许多问题都可归结为此问题)描述如下:生产者与消费者可以通过一个环形缓冲池联系起来,环形缓冲池由几个大小相等的缓冲块组成,每个缓冲块容纳一个产品。每个生产者可不断地每次往缓冲池中送一个生产产品,而每个消费者则可不断地每次从缓冲池中取出一个产品。 编写程序模拟多个生产者和多个消费者共享缓冲池的生产和消费过程。
【实验要求】
- 设计信号量用于进行生产者-消费者、生产者-生产者、消费者-消费者之间的同步控制;
- 创建多个(不少于5个)进程或者线程模拟生产者和消费者;
- 缓冲池大小定义在10-20,缓冲块中的产品为数据;
- 程序运行结果能实时显示缓冲池中生产和消费产品的过程,包括断点、推进时间、次序等,编程语言可选用C、C++、Java或者Python。
- 撰写实验报告。
一、 实验目的
- 能够按照进程同步机制的模拟设计五个进程的轮转式的进程同步调度方法;
- 观察进程的断点、每次推进的时间、进程执行的次序等,了解进程调度的基本设计方法;
- 通过设计、编程、上机调试掌握现代软件开发方法中并发程序设计的基本原理,在设计中培养创新意识。
四、 方案设计
设置两个资源信号量解决生产者/消费者问题,Full与Empty。由于有界缓冲区是一个临界资源,须互斥,所以设置一个互斥信号量Mutex。在生产者/消费者问题中,信号量实现两种功能。首先是生产产品和消费产品的计数器,其次是确保产品的生产者消费者动作同步的同步器。
生产者要生产一个产品时,首先对Full和Mutex进行P操作,申请资源。如果通过,生产一个产品,并送入缓冲区。然后对Mutex和Empty进行V操作,释放资源。消费者要消费一个产品时,首先对Empty和Mutex进行P操作,申请资源。如果通过,从缓冲区取出一个产品并消费。然后对Mutex和Full进行V操作,释放资源。
如果缓冲区中没有可用资源,就把申请资源的进程添加到等待队列队尾。等到有资源释放,等待队列中第一个进程被唤醒并取得这个资源。
实验环境:Windows10系统;devc++编译器。
五、 程序设计
#include <stdlib.h>
#include <windows.h>
#include <stdio.h>
#include <time.h>
#define P(S) WaitForSingleObject(S,INFINITE) // 定义P操作
#define V(S) ReleaseSemaphore(S,1,NULL) // 定义V操作
#define CONSUMER_NUM 3 //define消费者个数
#define PRODUCER_NUM 4 //define产者个数
#define BUFFER_NUM 20 //define缓冲区个数
typedef HANDLE Semaphore; //信号量的Windows原型
char *thing[8] = {"A", "B", "C", "D", "E", "F", "G","H"}; //生产和消费的产品名称
struct Buffer
{
int product[BUFFER_NUM]; // 缓冲区
int in,out; //in指示首空缓冲块序号,out指示首满缓冲块序号
}g_buf;
Semaphore Empty=0; //三个信号量
Semaphore Full;
Semaphore Mutex=0;
//消费者线程
DWORD WINAPI Consumer(LPVOID para)
{
// i表示第i个消费者
int i = *(int *)para; //利用para传入当前消费者的编号
int ptr; // 待消费的内容的指针
printf("消费者%1d: 需要资源\n", i);
int j=0;
while (j++<4){
//等待产品
P(Full);
//有产品,先锁住缓冲区
P(Mutex);
//记录消费的物品
ptr=g_buf.in;
//再移动缓冲区指针
g_buf.in= (g_buf.in+1)%BUFFER_NUM;
//让其他消费者或生产者使用
printf("消费者%01d: 需要buf[%d]=%s\n",i, ptr, thing[g_buf.product[ptr]]);
//消费完毕,并释放一个缓冲
printf("消费者%01d: 消费完毕%s\n", i,thing[g_buf.product[ptr]]);
V(Mutex);
V(Empty);
Sleep(1000*rand()%10+110);
}
return 0;
}
//生产者线程
DWORD WINAPI Producer(LPVOID para)
{
int i = *(int *)para - CONSUMER_NUM;
int ptr;
int data; //产品
int j=0;
while(j++<4)
{
data=rand()%8;
printf("生产者%01d: 生产出: %s!\n",i,thing[data]);
//等待存放空间
P(Empty);
//有地方,先锁住缓冲区
P(Mutex);
//记录消费的物品
ptr=g_buf.out;
//再移动缓冲区指针
g_buf.out =(g_buf.out+1)%BUFFER_NUM;
printf("生产者%01d: 放到缓冲区 buf[%d]=%s\n",i,ptr,thing[data]);
g_buf.product[ptr]=data;
//放好了完毕,释放一个产品
//让其他消费者或生产者使用
V(Mutex);
V(Full);
Sleep(1000/2*rand()%10+110);
}
return 0;
}
int main(int argc,char *argv[])
{
//前面为消费者线程,后面为生产者线程
HANDLE hThread[CONSUMER_NUM+PRODUCER_NUM]; //线程计数
srand(time(NULL));
rand();
DWORD tid;
int i=0;
//初始化信号量
Mutex=CreateSemaphore(NULL,1,1,"MutexOfConsumerAndProducer");
Empty=CreateSemaphore(NULL, BUFFER_NUM, BUFFER_NUM, "BufferSemaphone");
Full=CreateSemaphore(NULL,0,BUFFER_NUM,"ProductSemaphone");
if(!Empty||!Full||!Mutex)
{
printf("创建信号量错误!\n");
return -1;
}
int totalThreads=CONSUMER_NUM+PRODUCER_NUM;
//开启消费者线程
printf("=====开启消费者=====\n");
for(i=0;i<CONSUMER_NUM; i++)
{
hThread[i]=CreateThread(NULL, 0, Consumer, &i,0,&tid);
if(hThread[i])WaitForSingleObject(hThread[i],10);
}
printf("=====开启生产者=====\n");
for(;i<totalThreads;i++)
{
hThread[i]=CreateThread(NULL,0,Producer,&i,0,&tid);
if(hThread[i])WaitForSingleObject(hThread[i],10);
}
//生产者和消费者的执行
WaitForMultipleObjects(totalThreads,hThread,TRUE,INFINITE);
return 0;
}
六、 实验结果与分析
测试使用的消费者个数为3,生产者个数为4,缓冲区个数为20。生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区中取出一个产品并使用。生产者、消费者共享一个初始为空、大小为20的缓冲区。只有缓冲区没满时,生产者才能把产品放入缓冲区,否则必须等待。只有缓冲区不空时,消费者才能从中取出产品,否则必须等待。假设定义不同的消费者、生产者个数、缓冲区个数会有不同的运行结果。
七、 结论
本次课程设计通过模拟计算机操作系统中经典的“生产者—消费者问题”,巩固了我在操作系统原理课上所学的知识,加深了对操作系统中进程同步和互斥等问题,完成了多进程同步方法解决生产者-消费者问题全部过程,结果满足设计要求,但改进的空间还比较大。