计算机与信息学院
《操作系统与编译原理联合课程设计报告》
专题:操作系统部分
学生姓名:
学号:
专业班级:
指导教师:
2014 年 7 月
一、设计目标
多进程/线程编程:生产者-消费者问题。
设置两类进程/线程,一类为生产者,一类为消费者;建立缓冲区的数据结构;随机启动生产者或消费者;显示缓冲区状况;随着进程/线程每次操作缓冲区,更新显示。
二、设计思路
1.开发平台:Visual C++6.0
2.设计思路:
若干个生产者和若干个消费者共享一个有界缓冲区,生产者生产产品,消费者消费产品。
消费者进程与生产者进程随机切换。
生产者将产品生产出来后,存放到缓冲区中的空闲位置并将此缓冲区的标识置为满,若此时无空缓冲区,则进行等待。
消费者将标识为满的缓冲区中的产品取出,进行消费并将该缓冲区的标志位置为空,若此时无满的缓冲区,则进行等待。
由于消费者与生产者共享缓冲区资源,且缓冲区资源属于互斥资源,所以生产者和消费者需要按照一定的规则访问缓冲区,访问规则如下:
(1)当一个消费者访问缓冲区时其他消费者不允许访问缓冲区,同样的,当一个生产者访问缓冲区时其他生产者也不能访问缓冲区。
(2)当消费者访问缓冲区资源时生产者不能访问,反之,当生产者访问缓冲区资源时消费者不能访问。
(3)当缓冲区中无产品时,消费者不能访问;当缓冲区已满时,生产者不能访问缓冲区。
生产者与消费者问题伪代码如下:
VAR mutex, empty, full: semaphore := 1, n, 0 ;
in,out: integer := 0, 0 ;
Buffer: array [0..n-1] of item ;
Parbegin
Producer:
begin
repeat
produce an item in nextp;
wait(empty);
wait(mutex);
Buffer(in) := nextp;
in := (in + 1) mod n;
signal(mutex);
signal(full);
until false
end
Consumer:
begin
repeat
wait(full);
wait(mutex);
nextc = Buffer(out);
out := (out + 1) mod n;
signal(mutex);
signal(empty);
consume the item nextc;
until false
end
Parend
程序框架如下图所示:
本程序在具体实现方面与MFC结合,将生产者-消费者问题的具体过程动态展示了出来。
以下为界面的设计:
(1)界面拥有三个可输入的编辑框,分别对应着生产者数量、消费者数量以及缓冲区
大小。
(2)界面中有两个按钮,分别为“开始”和“停止”。
“开始”按钮用于获取生产值数量、消费者数量以及缓冲区大小,用于初始化以上三个量,并启动进程。
“停止”按钮用于停止程序的运行。
(3)三个只读的编辑框,用于动态显示消费者与生产者问题的详细过程。
MFC界面设计如下图所示:
三、核心代码
本程序的特色在于将生产者-消费者问题的内部的进程调度的细节以及缓冲区的状态变化动态的呈现在MFC的界面上。
且程序中还实现了随机调度生产者进程和消费者进程。
以下主要介绍随机算法以及生产者进程动态描述的源代码。
消费者的代码,与生产者代码相似。
1.随机启动进程函数:
调用c++函数库中定义产生随机数的函数,生成整数随机数。
使用取模的方法判断,若为奇数则创建并启动生产者进程;反之则创建并启动消费者进程。
srand(time(NULL));
for(i=0, j=0;i<CWork::producer_tcount || j< CWork::consumer_tcount;){
if (i>=CWork::producer_tcount){
CThreads[j]= ::CreateThread(NULL,0,CWork::consumer,this,0,&consumerID[j ]);
::CloseHandle(CThreads[j]);
j++;
}
else if (j>=CWork::consumer_tcount){
PThreads[i]=::CreateThread(NULL,0,CWork::producer,this,0,&producerID[i] );
::CloseHandle(PThreads[i]);
i++;
}
else{
int n = rand()%2;
if (n == 0){
PThreads[i]=CreateThread(NULL,0,CWork::producer,this,0,&producerID[ i]);
::CloseHandle(PThreads[i]);
i++;
}
else{
CThreads[j]=CreateThread(NULL,0,CWork::consumer,this,0,&consumerID[ j]);
::CloseHandle(CThreads[j]);
j++;
}
}
}
2.生产函数:
实现生产者生产产品的过程,即将空闲缓冲区位置更改为满的缓冲区位置。
void CWork::produce(CMy20112880P_CDlg* pDlg){
CString str1,str2,str3;
str1="生产";
pDlg->SetDlgItemText(IDC_ONE,str1);
pDlg->SetDlgItemInt(IDC_TWO,product_num);
buffer[in]=product_num;
product_num++;
buffer_state[in]='@';
show_p(pDlg);
in=(in+1)%size;
::Sleep(1000);
}
3.生产者生产状态显示函数:
实现显示生产者生产产品后,动态显示相关信息。
void CWork::show_p(CMy20112880P_CDlg* pDlg)
{
CString result;
CString str2 (": " );
CString str1;
CString str5("\r\n");
CString str4 (" <------生产");
for(int i=0;i<size;++i)
{
str1.Format("%d", i);
CString str3(buffer_state[i]);
result=result+str1+str2+str3 ;
if(i==in)
result=result + str4;
result=result+str5;
}
pDlg->SetDlgItemText(IDC_OUT,result);
::Sleep(1000);
}
四、设计结果及数据评测
测试1:输入的生产者数量、消费者数量、缓冲区大小分别为1,1,1。
即单生产者消费者单缓冲区。
测试截图如下:
单生产者消费者多缓冲区。
测试截图如下:
测试3:输入的生产者数量、消费者数量、缓冲区大小分别为4,4,1。
即多生产者消费者单缓冲区。
测试截图如下:
多生产者消费者多缓冲区。
测试截图如下:
五、设计中的问题及思考
本次课程设计是我学习编写代码以来实现的最为底层的程序。
第一次接触WINAPI的一下几个函数:CreateMutex,CreateSemaphore,WaitForSingleObject,ReleaseMutex,ReleaseSemaphore,CreateThread。
与此同时,通过本次课程设计,从之前的抽象理解到现在的具体实现,我对生产者-消费者问题有了更加深入的了解。
本次课程设计中遇到的难点有如下两个:
第一个难点是WINAPI的函数调用,每个函数调用时都需要设置很多不同的实参,如CreateThread函数的使用,首先需要理解每个参数的意义,其次就是了解这个函数的返回值类型。
由于本程序的设计支持多生产者多消费者,所以获取输入的生产者与消费者的数目之后,需要为生产者与消费者分别分配两个数组,一个数组存放的是进程,另一个数组存放的是进程ID号,两个数组同步操作,完成进程的创建与调用。
第二个难点是多线程与MFC的连接问题,及怎样将缓冲区状态、进程状态等动态的显示出在MFC界面上。
我通过SetDlgItemText函数对MFC编辑框中的内容进行更新,而更新的过程是这样的,以生产者为例:生产者进程函数调用生产函数,生产函数在调用状态显示函数。
这样就可以实现每个进程在执行状态时,进程状态以及其对应的缓冲区的状态都可以同步显示。
解决以上难点之后,对程序进行测试,分别测试了单生产者消费者单缓冲区、单生产者消费者多缓冲区、多生产者消费者单缓冲区、多生产者消费者多缓冲区几种不同的情况,其测试均得到正确结果,符合理论和实际。