1、 燕山大学课程设计阐明书 课程设计名称:操作系统 题目:多道程序缓冲区协调操作 (模仿生产者消费者问题) 课题负责人: 学院: 信息科学与工程学院 班级: 姓名: 学号:
2、 课题开发日期:1月13日 自评成绩: A 目录 1 概述--------------------------------------------------------------------------------------3 1.1 目--------------------------------------------------------3 1.2 重要完毕任务----------------------------------------------3 1.3 使用开发工
3、具、开发语言------------------------------------3 1.4 本软件解决重要问题 ---------------------------------------4 2 设计基本理念、概念和原理------------------------------------------------4 2.1 设计基本理念----------------------------------------------4 2.2 基本概念----------------------------------------------------4 2.3 基本原理----
4、5 3 总体设计----------------------------------------------------5 3.1基本技术路线:面向对象--------------------------------------------------------5 3.2模块关系及总体流程-------------------------------------------5 4 详细设计---------------------------------------------------
5、7 4.1 变量设计----------------------------------------------------7 4.2 线程设计--------------------------------------------------7 4.3 button按钮设计-------------------------------------------8 5编码设计----------------------------------------------------9 5.1开发环境---------------------------------------------
6、9 5.2注意事项----------------------------------------------------9 5.3重要代码设计------------------------------------------------9 PUTTER线程设计---------------------------------------------------9 MOVER1线程设计---------------------------------------------------10 GETTER1线程设计----------------------------
7、11 “开始”按钮设计--------------------------------------------------12 “结束”按钮设计--------------------------------------------------14 5.4解决重要难题----------------------------------------------16 6测试浮现问题及其解决方案-------------------------------16 7工程总结--------------------------------------
8、16 8参照文献----------------------------------------------------16 多道程序缓冲区协调操作演示程序设计阐明书 1 概述 1.1 目 计算机操作系统是计算机系统中最不可缺少,最惯用软件,也是核心,最接近于计算机硬件软件。其特点是内容繁多,概念抽象,因而导致理解困难,掌握不易。本软件重要目是通过直观演示,使学生可以感性明白掌握多道程序及其进程同步和互斥程序设计基本办法。 1.2 重要完毕任务 (1) 可随机产生字符数据,由生产者put操作不断将生产字符数据放入容器1(Buffer1)
9、中。 (2) 通过搬运者Move1操作要不断地将容器1(Buffer1)数据取到容器2(Buffer2)中。 (3) 通过搬运者Move2操作要不断地将容器1(Buffer1)数据取到容器3(Buffer3)中。 (4) 通过消费者1GET操作不断从容器2(buffer2)中取出数据 (5) 通过消费者2GET操作不断地从容器3(Buffer3)中取出数据。 (6) 生产者,搬运者,消费者数目,buffer容量可自己设定,但数目不适当过多;默以为生产者5,消费者1为5,消费者2为5,Move1为2,Move2为2,buffer1容量为10,buffer2容量为10,buffer3容量
10、为10。 (7) PUT、Move1、Move2、GET1,GET2每次操作一种数据,在操作过程中数据不丢失,每个Buffer每次只能接受一种PUT或一种Move或一种Get,各种操作不能同步操作同一BUFFER。 (8) 可以实时显示Buffer操作过程,以及每个Buffer当前放入数据,每个buffer中数据个数。 (9) 可以对生产者,搬运者,消费者速度进行自由控制。 (10) 当程序运营开始后,计时器就开始计时,直到运营结束,显示运营总时间。 (11) 运营结束后,可以汇总总运营时时间、已生产产品数、消费者1已消费产品数、消费者2已消费产品数、总消费产品数。 1.3使
11、用开发工具、开发语言 开发工具:VS 开发语言:C++ C++是面向对象一种编程语言,窗口程序设计中MFC已经将windows最底层API函数以类形式封装好,使用以便。 其特点有:1.面向对象; 2.平台无关性; 3.安全性; 4.健壮性; 1.4本软件解决重要问题 对Buffer操作多线程同步问题,运用操作系统P、V原语操作和C++语言Thread线程对put、move、get等多线程进行协调解决,实现了多线程并发执行原理。用程序演示了操作系统中典型生产者和消费者问题。 2 设计基本理念、概念和原理 2.1 设计基本
12、理念 使用VS创立了一种基本对话框类,并在对话框中添加了基本需要所有控件: (1) buffer1,buffer2,buffer3三个LISTBOX控件,用于显示各个buffer中当前内容。 (2) 添加了3个编辑框控件,分别用于对3个容器(buffer)容量控制。 (3) 添加1个编辑框控件用于输入数值拟定线程执行速度。 (4) 添加5个编辑框控件,用于对生产者,移动物流,消费者数量控制。 五个线程用于对buffer容器控制(PUTTER,MOVER1,MOVER2,GETTER1,GETTER2): (1) PUTTER线程产生随机字符,并放入buffer1中,实现生产
13、者生产过程。 (2) MOVER1,MOVER2线程分别将buffer1中数据移动至buffer2 和buffer3中。 (3) GETTER1,GETTER2线程分别将buffer2和buffer3中数据字符移出,实现消费者消费过程。 各种变量分别记录需要显示数据: (1) 三个变量分别记录buffer1,buffer2,buffer3中数据并实时显示出来。 (2) 五个变量进行数据汇总,显示最后运营总时间,生产者生产数量,消费者消费数量 。 通过MFC对话框中按钮实现对所有线程控制: (1) “开始”按钮:开始所有线程,实现多线程程序同步
14、 (2) “结束”按钮:结束所有线程,并显示数据汇总状况。 2.2 基本概念 面向对象,进程,线程,线程同步,线程互斥,多道程序。 2.3 基本原理 典型生产者与消费者同步原理,通过互斥体和互斥信号来实现线程等待,线程间同步问题,线程之间协调问题。 3.总体设计 3.1基本技术路线:面向对象 运用面向对象设计理念,设计所规定PUTTER,MOVER1,MOVER2,GETTER1,GETTER2五个线程,达到信号量控制,变量值拟定,实现BUFFER一次只能操作一种动作,实现线程同步,阻塞以及她们之间协调问题。 Pca对话框 类:主界面设计、显示数据、过程演示 P
15、UTTER线程: 生产者:执行put操作 MOVER1线程: 搬运者1:执行move1操作 GETTER1线程: 消费者1:执行get 操作 MOVER2线程: 搬运者2:执行move2操作 GETTER2线程: 消费者2:执行get操作 3.2 模块关系及总体流程 图1. 模块关系 参数设定(或使用默认数据) 开始 运营(速度控制) 结束 动态显示每个buffer中字符数据内容,移动过程以及数量 数据汇总显示 退出
16、 图2.总体流程 4.详细设计 4.1变量设计 g_hMutex1,g_hMutex2,g_hMutex3:三个互斥体,分别控制一次只能对buffer实现一次操作。 g_hFullItems1,g_hFullItems2,g_hFullItems3 g_hEmptyItems1,g_hEmptyItems2,g_hEmptyItems3: 六个信号量,分别控制buffer中与否有空闲空间以及与否有数据可供移动,并进行互斥操作。
17、 clock_t类型 start,finish变量,通过调用clock()函数得到线程运营总时间。 struct PThread{ int ptid; CpacDlg * dlg; }; 定义线程构造体,用于线程通过构造体参数调用窗口类,从而实现线程对窗口控制。 SIZE_1 ,SIZE_2 ,SIZE_3:编辑框控件添加变量,从而实现动态对容器buffer容量控制。 SPEED:控件添加变量,实现对线程速度控制。 Produce_Num ,Consumer1_Num ,Consumer2_Num ,Move1
18、Num ,Move2_Num:控件添加变量,实现对生产者,消费者,物流移动数量控制。 Con1_Num ,Pro_Num ,Con2_Num ,Con_Num:控件添加变量,实现最后数据记录汇总显示。 Buffer1,buffer2,buffer3:ListBox控件添加控制变量,用于显示各个buffer中字符数据内容。 4.2线程设计 PUTTER线程产生随机字符,并放入buffer1中,实现生产者生产过程。 DWORD WINAPI PUTTER(LPVOID para) //PUT线程 其参数为 LPVOID para
19、 ,在创立线程时通过 P_hThreads[i] =CreateThread(NULL,0,PUTTER,(LPVOID)&pthread0,0,&putID[i]); 语句,第四个参数传递了构造体参数,将当前对话框窗口类指针传递给线程函数,通过 PThread * pthread = (PThread *)para;//规范化参数 CpacDlg * dlg = pthread->dlg; 语句来实现对当前对话框窗口中所有参数调用。 MOVER1,MOVER2,GETTER1,GETTER2线程与PUTTER线程类似。 4.3 button按钮设计 “开始”按钮
20、 创立每个互斥体,互斥信号以及线程。 void CpacDlg::OnBnClickedButton1() { // TODO:在此添加控件告知解决程序代码 UpdateData(1); g_hMutex1 = ::CreateMutex(NULL,FALSE,NULL); //buff1互斥锁 g_hFullItems1 = ::CreateSemaphore(NULL,0,SIZE_1,NULL); //buff1信号量 g_hEmptyItems1 = ::CreateSemaphore(NULL,SIZE_1,
21、
SIZE_1,NULL);
。。。。。。。。。。。。。。。
。。。。。。。。。。。。。。
//创立PUT线程
for (int i=0;i 22、//////////////
if(P_hThreads[i] == NULL) MessageBox(TEXT("线程创立错误!"));
}
。。。。。。。。。
。。。。。。。。。
}
“结束”按钮: 结束每一种线程,并进行数据汇总显示到对话框上。
void CpacDlg::OnBnClickedButton2()
{
// TODO:在此添加控件告知解决程序代码
//UpdateData(1);
DWORD KP_Thread[10];
for(int i=0;i 23、{
TerminateThread(P_hThreads[i],KP_Thread[i]);
}
。。。。。。。。。。
}
5 编码设计
5.1 开发环境
Windows7 ,已安装了VS。由于本软件是用C++语言开发,因此跨平台性能比较好。本程序使用重要类、函数,都是自己写,用到windows builder插件,用于设计界面。
5.2 注意事项
1. 编写代码要有良好格式,有良好注释,此程序要特别注意
2.注意变量使用,防止地址引用错误产生
5.3 重要代码设计
PUTTER线程设计:
DWORD WINAPI PUTTER(LPV 24、OID para) //PUT线程
{
PThread * pthread = (PThread *)para;//规范化参数
CpacDlg * dlg = pthread->dlg;
int i = pthread->ptid;
int j=dlg->SPEED;
int speed = 1000*j;
LARGE_INTEGER nFrequency;//设立随机数种子
if(::QueryPerformanceFrequency(&nFrequency))
{
// 如果支持高性能精度计数器,则使用其初始化随机种子(微 25、秒级)
LARGE_INTEGER nStartCounter;
::QueryPerformanceCounter(&nStartCounter);
::srand((unsigned)nStartCounter.LowPart);
}
else // 否则使用当前系统时间初始化随机种子(毫秒级)
{
::srand((unsigned)time(NULL));
}
while(true)
{
CString buf;
WaitForSi 26、ngleObject(g_hEmptyItems1,INFINITE); //进行P操作
WaitForSingleObject(g_hMutex1,INFINITE);
//产生随机字符(A-Z)
char ch;
ch = char ( (rand() % 26) + 65 );
buf.Format(_T("put-->%c"),ch);
dlg->buffer1.InsertString(0,buf);
dlg->Pro_Num++; //拟定生产总数量
int buf1c; 27、 //拟定当前buffer1中产品数量
CString str;
buf1c = dlg->buffer1.GetCount();
str.Format(_T("%d个"),buf1c);
dlg->Buffer_C.SetWindowTextW(str); //在窗口中显示当前buffer1数据数量
::Sleep(speed); //速度设立
ReleaseMutex(g_hMutex1); //进 28、行V操作
ReleaseSemaphore(g_hFullItems1,1,NULL);
}
return 0;
}
MOVER1线程设计:
DWORD WINAPI MOVER1(LPVOID para) //MOVE1线程
{
PThread * pthread = (PThread *) para; //规范化参数
CpacDlg * dlg = pthread->dlg;
int i = pthread->ptid;
int j=(dlg->SPEED);
int speed =1000*j;
while(tr 29、ue)
{
WaitForSingleObject(g_hFullItems1,INFINITE); //进行P操作
WaitForSingleObject(g_hEmptyItems2,INFINITE);
WaitForSingleObject(g_hMutex1,INFINITE);
WaitForSingleObject(g_hMutex2,INFINITE);
//将buffer1中数据移至buffer2中
CString cs;
int n;
n = dl 30、g->buffer1.GetCount();
dlg->buffer1.GetText(n-1,cs);
dlg->buffer1.DeleteString(n-1);
CString cc;
cc.Format(_T("move1--"));
cs = cc + cs;
dlg->buffer2.InsertString(0,cs);
int buf1c; //拟定当前buffer1中产品数量
CString str;
buf1c = dlg->buffer1.GetCount();
st 31、r.Format(_T("%d个"),buf1c);
dlg->Buffer_C.SetWindowTextW(str); //显示当前buffer1中产品数量
int buf2c; //拟定当前buffer2中产品数量
CString str1;
buf2c = dlg->buffer2.GetCount();
str1.Format(_T("%d个"),buf2c);
dlg->Buf_C1.SetWindowTextW(str1); //显示当前buffer2中产品数量
::Sle 32、ep(speed); //设立速度
ReleaseMutex(g_hMutex2); //进行V操作
ReleaseMutex(g_hMutex1);
ReleaseSemaphore(g_hFullItems2,1,NULL);
ReleaseSemaphore(g_hEmptyItems1,1,NULL);
}
return 0;
}
GETTER1线程设计:
DWORD WINAPI GETTER1(LPVOID para) 33、//GET1线程
{
PThread * pthread = (PThread *)para;//规范化参数
CpacDlg * dlg = pthread->dlg;
int i = pthread->ptid;
int j=(dlg->SPEED);
int speed =1000*j ;
while(true)
{
WaitForSingleObject(g_hFullItems2,INFINITE); //进行P操作
WaitForSingleObject(g_hMutex2,INFINITE);
// 34、移出buffer2中数据
CString cs;
int n;
n = dlg->buffer2.GetCount();
dlg->buffer2.GetText(n-1,cs);
dlg->buffer2.DeleteString(n-1);
dlg->Con1_Num = dlg->Con1_Num + 1; //消费者1消费产品数量加1
int buf2c; //拟定当前buffer2中产品数量
CString str1;
buf2c = dlg->buffer2.Ge 35、tCount();
str1.Format(_T("%d个"),buf2c);
dlg->Buf_C1.SetWindowTextW(str1);
::Sleep(speed); //设立速度
ReleaseMutex(g_hMutex2); //进行V操作
ReleaseSemaphore(g_hEmptyItems2,1,NULL);
}
return 0;
}
“开始”按钮设计:
void CpacDlg:: 36、OnBnClickedButton1()
{
// TODO:在此添加控件告知解决程序代码
UpdateData(1);
g_hMutex1 = ::CreateMutex(NULL,FALSE,NULL); //buff1互斥锁
g_hFullItems1 = ::CreateSemaphore(NULL,0,SIZE_1,NULL); //buff1信号量
g_hEmptyItems1 = ::CreateSemaphore(NULL,SIZE_1,
37、 SIZE_1,NULL);
g_hMutex2 = ::CreateMutex(NULL,FALSE,NULL);
g_hFullItems2 = ::CreateSemaphore(NULL,0,SIZE_2,NULL); //buff2互斥锁
g_hEmptyItems2 = ::CreateSemaphore(NULL,SIZE_2,
SIZE_2,NULL);
g_hMutex3 = ::CreateMutex(NULL,FALSE,NULL);
38、
g_hFullItems3 = ::CreateSemaphore(NULL,0,SIZE_3,NULL); //buff3互斥锁
g_hEmptyItems3 = ::CreateSemaphore(NULL,SIZE_3,
SIZE_3,NULL);
start = clock(); //获得线程开始时间
//创立PUT线程
for (int i=0 39、i 40、 for(int j=0;j 41、
pthread2.dlg = this;
pthread2.ptid = k;
M2_hThreads[k]
=CreateThread(NULL,0,MOVER2,(LPVOID)&pthread2,0,&move2ID[k]);
if(M2_hThreads[k] == NULL) MessageBox(TEXT("线程创立错误!"));
}
//创立GET1和GET2线程
for (int m=0;m 42、lg = this;
pthread3.ptid = m;
C1_hThreads[m]
=CreateThread(NULL,0,GETTER1,(LPVOID)&pthread3,0,&get1ID[m]);
if(C1_hThreads[m] == NULL) MessageBox(TEXT("线程创立错误!"));
}
for (int n=0;n 43、 n;
C2_hThreads[n]
=CreateThread(NULL,0,GETTER2,(LPVOID)&pthread4,0,&get2ID[n]);
if(C2_hThreads[n] == NULL) MessageBox(TEXT("线程创立错误!"));
}
}
“结束”按钮设计:
void CpacDlg::OnBnClickedButton2()
{
// TODO:在此添加控件告知解决程序代码
//UpdateData(1);
DWORD 44、 KP_Thread[10];
for(int i=0;i 45、[i],&KM1_Thread[i]);
TerminateThread(M1_hThreads[i],KM1_Thread[i]);
//ExitThread(KM1_Thread[i]);
}
DWORD KM2_Thread[1];
for(int i=0;i 46、
}
DWORD KC1_Thread[10];
for(int i=0;i 47、double duration;
finish = clock(); //得到线程结束时间
duration = double ((finish - start)/ CLOCKS_PER_SEC); //得到线程运营总时间
CString str4;
str4.Format(_T("%2.4f秒"),duration);
CLOCK.SetWindowTextW(str4); //输出线程运营总时间
CString str;
s 48、tr.Format(_T("%d个"),Pro_Num);
Pro_NUM.SetWindowTextW(str); //输出生产者最后身产总数
CString str1;
str1.Format(_T("%d个"),Con1_Num);
Con1_NUM.SetWindowTextW(str1); //输出消费者1消费产品总数
CString str2;
str2.Format(_T("%d个"),Con2_Num);
Con2_NUM.SetWindow 49、TextW(str2); //输出消费者2消费产品总数
CString str3;
Con_Num = Con1_Num + Con2_Num;
str3.Format(_T("%d个"),Con_Num);
Con_NUM.SetWindowTextW(str3); //输出总消费产品数量
}
5.4 解决重要难题
(1) 生产搬运消费过程显示;
(2) 多线程之间同步、互斥问题;
(3) Buffer数据安全问题;
(4) 程序运营时间 50、显示问题
(5) 对各个线程放入取出速度任意控制问题;
6 测试浮现问题及其解决方案
(1) 测试过程中浮现了各种Put、Move或get不能互斥访问Buffer,通过查阅书籍重复测试,经教师、助教教师指点,恰当设立线程sleep()时间基本解决。
(2) 初始设计时,界面设计感到很不满意,经多次改进,添加元素,固然,由于时间因素,只达到了初步满意限度,尚有待进一步改进。
设计过程中浮现了地址访问错误,通过教师指点,不断调试,添加了变量,使得地址错误得到解决。
7 工程总结
通过操作系统课程设计这一小项目实践,使得我获得诸多收获,弥补了自己局限性。使我对操作系统中多线程并