燕山大学课程设计说明书

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

燕山大学课程设计说明书
课程设计名称:操作系统
题目:多道程序缓冲区协调操作
班级:11级计算机应用1班
开发小组名称:多道程序缓冲区协调操作的程序设计课题负责人:吴楠
课题组成员:姓名吴楠学号 110104010058
班级应用1班自评成绩
姓名王乐学号 110104010045
班级应用1班自评成绩
课题开发日期:2014.1.10
1 概述
1.1 课程设计目的
通过编写一个生产者消费者的实例,了解多线程的创建,运行原理,通过信号量机制的运用了解各线程间的协调工作机制;通过实现界面编程,了解MFC 编程思想。

1.2 主要完成的任务
如下图所示,有多个PUT 操作要不断循环地向Buffer1送字符数据,有Move1操作不断地将Buffer1的数据取到Buffer2,Move2操作不断地将Buffer1的数据取到Buffer3,有多个GET 操作要不断地从Buffer2和Buffer3中取数据。

PUT 、 MOVE 、 GET 每次操作一个数据,为了在操作的过程中要保证数据不丢失, 每个Buffer 每次只能接受一个PUT 或一个Move 或一个Get 。

运用进程同步和互斥机制设计一个多道程序完成上述操作。

图1 Buffer 操作
(1) 可以随机产生字符数据,由put 操作放入Buff1,buffer 中容量单位是字符。

(2)提供良好图形界面,显示Buffer 的操作过程。

(3) 可以设定各Buffer 的容量、PUT 、GET 、Move 操作的个数;
(4) 可以设定PUT 、GET 、Move 操作的速度;
(5) 实时显示每个Buffer 中数据的个数和数据的内容,空闲Buffer 的空间的个数;
(6) 实时显示线程、进程所处于等待(阻塞)状态的个数
(7)程序运行结束,显示汇总数据:
总的运行时间;
Buffer 中数据的个数;
已放入BUFFER 的数据个数;
已取出的数据个数;
平均每个buffer 中的数据个数。

Put Move2 Buff1 Buff2 Buff3 Get Move1 Get
1.3 课程设计使用的开发语言和工具
语言:C++
开发环境:Visual Studio 2008及其开发环境下的MFC平台。

1.4 解决的主要问题
(1)MFC界面设计
(2)模拟生产者消费者的互斥操作
(3)各信号量的使用
2 使用的基本概念和原理
2.1 MFC
MFC是Windows下程序设计的最流行的一个类库,它合理的封装了WIN32 API函数,并设计了一套方便的消息映射机制。

2.2 MFC的消息实现机制
在MFC的框架结构下,可以进行消息处理的类的头文件里面都会含有DECLARE_MESSAGE_MAP()宏,这里主要进行消息映射和消息处理函数的声明。

所有能够进行消息处理的类都是基于CCmdTarget类的,也就是说CCmdTarget类是所有可以进行消息处理类的父类。

CCmdTarget类是MFC处理命令消息的基础和核心。

2.3 线程
线程是程序独立运行的基本单位,一个程序通过执行多个线程可以提高机器本身资源的利用率,同时也可以完成多任务并行运行的操作。

2.4 信号量
信号量是一个在一定范围内变化的整形数据,用来表示一种临界资源,线程通过信号量的值来确定自己的状态是执行还是挂起,各线程间也是通过信号量机制来协调运行顺序一起完成任务。

3 总体设计
3.1基本的技术路线
确定基本技术路线为面向对象程序设计,使用MFC编写程序,建立基本对话框。

在对话框中设立生产者、MOVE、消费者板块,各板块内的按钮能控制各自线程的建立、暂停以及相关数据的设定。

3.2软件的总体结构
图2 总体结构
3.3模块关系
主要分为显示模块、数据模块和线程模块。

模块之间的关系显示模块显示的数据模块的内容,而数据模块又影响线程模块,三个模块互相影响,并根据各自的变化,实时显示并协调工作。

3.4总体流程
通过创建生产者线程往BUFFER1中投放随机产生的字符,创建MOVE1线程和MOVE2线程将BUFFER1中的字符移动到BUFFER2和BUFFER3中,创建消费者线程从BUFFER2和BUFFER3中取出字符消费。

建立信号量,因为生产者线程和MOVE1、MOVE2线程共同使用临界资源BUFFER1,所以用Mutex1信号量来协调生产者线程和MOVE1和MOVE2线程;因为MOVE1线程和MOVE2线程共同使用临界资源BUFFER1,因为MOVE1线程和消费者线程GET1共同使用临界资源BUFFER2,MOVE2线程和消费者线程GET2共同使用临界资源BUFFER2,所以用Mutex23信号量来协调MOVE1线程和消费者线程GET1,用Mutex3信号量来协调MOVE2线程和消费者线程GET2。

4 详细设计
4.1r ThreadInfo结构体
typedef stuct ThreadInfo{
CListBox *pList;
}thread,*lpthread;
此结构体用来保存在线程建立时往线程中传送的参数信息(如该线程指定的操作控件),以供在本线程中使用。

4.2 线程操作函数
4.2.1 执行函数的声明
//声明PUT线程。

DWORD WINAPI ThreadPut(LPVOID lpParameter);
//声明MOVE1线程。

DWORD WINAPI ThreadMove1(LPVOID lpParameter);
//声明MOVE2线程。

DWORD WINAPI ThreadMove2(LPVOID lpParameter);
//声明GET线程,主要区别是能分别输出该消费的字符是哪个消费者消费的。

DWORD WINAPI ThreadGet(LPVOID lpParameter);
4.2.2 各信号量的定义
Mutex = CreateMutex(NULL,false,NULL);
Mutex1 = CreateMutex(NULL,false,NULL);
Mutex2 = CreateMutex(NULL,false,NULL);
SemaphoreEmpty1=CreateSemaphore(NULL,m_buffer1,m_buffer1,NULL);
SemaphoreFULL1=CreateSemaphore(NULL,0,m_buffer1,NULL);
SemaphoreEmpty2=CreateSemaphore(NULL,m_buffer2,m_buffer2,NULL);
SemaphoreFULL2=CreateSemaphore(NULL,0,m_buffer2,NULL);
SemaphoreEmpty3=CreateSemaphore(NULL,m_buffer3,m_buffer3,NULL);
SemaphoreFULL3=CreateSemaphore(NULL,0,m_buffer3,NULL);
4.2.3各线程的函数接口的定义及算法描述
PUT线程函数接口:
DWORD WINAPI ThreadPut(LPVOID lpParameter)//put线程的入口函数
{
while(toend)
{
Sleep(speed1);
CString str;
str= rand()%128; //生成随机字符
WaitForSingleObject(SemaphoreEmpty1,INFINITE);//申请空间实现P(Empty1)
WaitForSingleObject(mutex1,INFINITE); //申请互斥信号
m_list1->InsertString(-1,str);
b1++;
buffernum1++;
CString s,s1,buff;
s.Format("%d",b1);
s1.Format("%d",buffer1-b1);//剩余的容量
full1_static->SetWindowText(s);
empty1_static->SetWindowText(s1);
ReleaseMutex(mutex1); //释放信号
ReleaseSemaphore(SemaphoreFULL1,1,NULL); //实现V(FULL1)
buff.Format("%s%d","已放入BUFFER的数据个数:",buffernum1);
product_static->SetWindowText(buff);
buffere1_progress->SetPos((m_list1->GetCount())*100/buffer1);
}
return 1;
}
MOVE1线程函数接口:
DWORD WINAPI ThreadMove1(LPVOID lpParameter) //move1线程函数{
while(toend)
{
Sleep(speed2);
CString str;
WaitForSingleObject(SemaphoreFULL1,INFINITE);
WaitForSingleObject(SemaphoreEmpty2,INFINITE);
WaitForSingleObject(mutex2,INFINITE);
WaitForSingleObject(mutex1,INFINITE);
m_list1->GetText(0,str);
m_list1->DeleteString(0);
b1--;
CString s,s1;
s.Format("%d",b1);
s1.Format("%d",buffer1-b1);
full1_static->SetWindowText(s);
empty1_static->SetWindowText(s1);
m_list2->InsertString(-1,str);
b2++;
CString s2,s3;
s2.Format("%d",b2);
s3.Format("%d",buffer2-b2);
full2_static->SetWindowText(s2);
empty2_static->SetWindowText(s3);
ReleaseMutex(mutex1);
ReleaseSemaphore(SemaphoreEmpty1,1,NULL);
ReleaseMutex(mutex2);
ReleaseSemaphore(SemaphoreFULL2,1,NULL);
buffere1_progress->SetPos((m_list1->GetCount())*100/buffer1);
buffere2_progress->SetPos((m_list2->GetCount())*100/buffer2);
}
return 1;
}
MOVE2线程函数接口:
DWORD WINAPI ThreadMove2(LPVOID lpParameter)//move2线程函数{
while(toend)
{
Sleep(speed3);
CString str;
WaitForSingleObject(SemaphoreEmpty3,INFINITE);
WaitForSingleObject(SemaphoreFULL1,INFINITE);
WaitForSingleObject(mutex3,INFINITE);
WaitForSingleObject(mutex1,INFINITE);
m_list1->GetText(0,str);
m_list1->DeleteString(0);
b1--;
CString s1,s3;
s1.Format("%d",b1);
s3.Format("%d",(buffer1-b1));
full1_static->SetWindowText(s1);
empty1_static->SetWindowText(s3);
m_list3->InsertString(-1,str);
b3++;
CString s4,s5;
s4.Format("%d",b3);
s5.Format("%d",(buffer3-b3));
full3_static->SetWindowText(s4);
empty3_static->SetWindowText(s5);
ReleaseMutex(mutex1);
ReleaseMutex(mutex3);
ReleaseSemaphore(SemaphoreEmpty1,1,NULL);
ReleaseSemaphore(SemaphoreFULL3,1,NULL);
buffere1_progress->SetPos((m_list1->GetCount())*100/buffer1);
buffere3_progress->SetPos((m_list3->GetCount())*100/buffer3);
}
return 1;
}
GET线程函数的接口:
DWORD WINAPI ThreadGet(LPVOID lpParameter)//get2线程
{
while(toend)
{
Sleep(speed4);
CString str;
WaitForSingleObject(SemaphoreFULL3,INFINITE);
WaitForSingleObject(mutex3,INFINITE);
m_list3->DeleteString(0);
b3--;
buffernum2++;
CString s4,s5,buff1;
s4.Format("%d",b3);
s5.Format("%d",buffer3-b3);
full3_static->SetWindowText(s4);
empty3_static->SetWindowText(s5);
buff1.Format("%s%d","已放已取的数据个数:",buffernum2);
consumer_static->SetWindowText(buff1);
ReleaseMutex(mutex3);
ReleaseSemaphore(SemaphoreEmpty3,1,NULL);
finish=clock();
m_time=((int)(finish-start));
CString str2;
str2.Format("%s%d%s","总生产累计用时为:",m_time,"毫秒");
time_static->SetWindowText(str2);
buffere3_progress->SetPos((m_list3->GetCount())*100/buffer3);
}
return 1;
}
5 编码设计
5.1 开发环境的设置和建立
如图所示选择MFC 应用程序,输入项目名称后单击“确定”。

图1 新建项目
再选择“基于对话框”,之后单击“完成”建立项目。

如图2:
图2 MFC程序向导
开发环境建立完毕。

5.2程序设计时要注意的事项
申请信号时P、V原语的顺序。

例如:创建move1线程时,首先要先申请buffer2的空区,buffer1的数据,再进行申请互斥信号mutex1、mutex2,否则将产生不可预知的结果。

代码如下:
WaitForSingleObject(SemaphoreFULL1,INFINITE);
WaitForSingleObject(SemaphoreEmpty2,INFINITE);
WaitForSingleObject(mutex2,INFINITE);
WaitForSingleObject(mutex1,INFINITE);
5.3关键控件的特点和使用
控件MFC类描述
按钮CButton 用来产生某种行为的按钮,以及复选
框、单选钮和组框
编辑框CEdit 用于键入文本
列表CListCtrl 显示文本及其图标列表的窗口
列表框CListBox 包括一系列字符串的列表
静态文本CStatic 常用于为其它控件提供标签
表一关键控件名称、作用
5.4主要程序的代码设计及注释
5.4.1开始执行按钮相关代码
void COs1Dlg::Onbegin() //开始执行
{
// TODO: Add your control notification handler code here
//UpdateData(1);
m_list1=(CListBox *)GetDlgItem(IDC_LIST1);
m_list2=(CListBox *)GetDlgItem(IDC_LIST2);
m_list3=(CListBox *)GetDlgItem(IDC_LIST3);
//信号初始化
SemaphoreEmpty1=CreateSemaphore(NULL,m_buffer1,m_buffer1,NULL);
SemaphoreFULL1=CreateSemaphore(NULL,0,m_buffer1,NULL);
mutex1 = CreateMutex(NULL,false,NULL);
SemaphoreEmpty2=CreateSemaphore(NULL,m_buffer2,m_buffer2,NULL); SemaphoreFULL2=CreateSemaphore(NULL,0,m_buffer2,NULL);
mutex2 = CreateMutex(NULL,false,NULL);
SemaphoreEmpty3=CreateSemaphore(NULL,m_buffer3,m_buffer3,NULL); SemaphoreFULL3=CreateSemaphore(NULL,0,m_buffer3,NULL);
mutex3 = CreateMutex(NULL,false,NULL);
int j =put;
toend=1;
clock_t pp;
pp=clock();
start=pp;
while(j!=0)//创建put类线程
{
Threadp[j-1]=CreateThread(NULL,0,ThreadPut,NULL,0,&ThreadP[j-1]);
j--;
}
int i = move1;
while(i!=0)//创建move1类线程
{
Threadm1[i-1]=CreateThread(NULL,0,ThreadMove1,NULL,0,&ThreadM1[i-1]);
i--;
}
int k= move2;
while(k!=0)//创建move2类线程
{
Threadm2[k-1]=CreateThread(NULL,0,ThreadMove2,NULL,0,&ThreadM2[k-1]);
k--;
}
int t= get;
while(t!=0)//创建move1类线程
{
Threadg[t-1]=CreateThread(NULL,0,ThreadGet,NULL,0,&ThreadG[t-1]);
t--;
}
m_begin.EnableWindow(false);//对各个按钮可操作不开操作的控制
m_stop.EnableWindow(true);
m_suspend.EnableWindow(true);
m_continue.EnableWindow(true);
}
5.4.2put线程相关代码
DWORD WINAPI ThreadPut(LPVOID lpParameter)//put线程
{
while(toend)
{
Sleep(speed1);
CString str;
str= rand()%128; //生成随机字符
WaitForSingleObject(SemaphoreEmpty1,INFINITE);//申请空间
WaitForSingleObject(mutex1,INFINITE); //申请互斥信号
m_list1->InsertString(-1,str);
b1++;
buffernum1++;
CString s,s1,buff;
s.Format("%d",b1);
s1.Format("%d",buffer1-b1);
full1_static->SetWindowText(s);
empty1_static->SetWindowText(s1);
ReleaseMutex(mutex1); //释放信号
ReleaseSemaphore(SemaphoreFULL1,1,NULL);
buff.Format("%s%d","已放入BUFFER的数据个数:",buffernum1);
product_static->SetWindowText(buff);
}
return 1;
}
5.4.3 move1线程相关代码
DWORD WINAPI Threadmove1(LPVOID lpParameter)//move1线程
{
int n=move1num;
move1num++;
CString str,str2,str1;
while(toend)
{
Sleep(move1speed);
WaitForSingleObject(SemaphoreFULL1,INFINITE);
WaitForSingleObject(SemaphoreEmpty2,INFINITE);
WaitForSingleObject(hMutex1,INFINITE);
WaitForSingleObject(hMutex2,INFINITE);
str2.Format("%s%d%s","数据个数:",list1->GetCount(),"个"); //动态显示数据个数、空区个数
buf1data_num->SetWindowText(str2);
str1.Format("%s%d%s"," 空区个数:",buffer1-list1->GetCount(),"个");
buf1emp_num->SetWindowText(str1);
list1->GetText(0,str);
list2->AddString(str);
list1->DeleteString(0);
str2.Format("%s%d%s","数据个数:",list2->GetCount(),"个");
buf2data_num->SetWindowText(str2);
str2.Format("%s%d%s"," 空区个数:",buffer2-list2->GetCount(),"个");
buf2emp_num->SetWindowText(str2);
ReleaseMutex(hMutex1);
ReleaseMutex(hMutex2);
ReleaseSemaphore(SemaphoreEmpty1,1,NULL);
ReleaseSemaphore(SemaphoreFULL2,1,NULL);
if(ifend==1)
{
ifend=0;
return 1;
}
}
return 1;
}
5.4.4 move2线程相关代码
DWORD WINAPI Threadmove2(LPVOID lpParameter)//move2线程
{
int p=move2num;
move2num++;
while(toend)
{
Sleep(move2speed);
CString str,str2;
WaitForSingleObject(SemaphoreFULL2,INFINITE);
WaitForSingleObject(SemaphoreEmpty3,INFINITE);
WaitForSingleObject(hMutex2,INFINITE);
WaitForSingleObject(hMutex3,INFINITE);
list2->GetText(0,str);
list3->AddString(str);
list2->DeleteString(0);
str2.Format("%s%d%s","数据个数:",list2->GetCount(),"个");//动态显示空区个数,数据个数
buf2data_num->SetWindowText(str2);
str2.Format("%s%d%s"," 空区个数:",buffer2-list2->GetCount(),"个");
buf2emp_num->SetWindowText(str2);
str2.Format("%s%d%s","数据个数:",list3->GetCount(),"个");
buf3data_num->SetWindowText(str2);
str2.Format("%s%d%s"," 空区个数:",buffer3-list3->GetCount(),"个");
buf3emp_num->SetWindowText(str2);
ReleaseMutex(hMutex2);
ReleaseMutex(hMutex3);
ReleaseSemaphore(SemaphoreEmpty2,1,NULL);
ReleaseSemaphore(SemaphoreFULL3,1,NULL);
if(ifend==1)
{
ifend=0;
return 1;
}
}
return 1;
}
5.4.5get线程相关代码
DWORD WINAPI Threadget(LPVOID lpParameter)//get线程
{
int m=getnum;
getnum++;
while(toend)
{ CString str2;
Sleep(getspeed);
WaitForSingleObject(SemaphoreFULL3,INFINITE);//申请需要的信号
WaitForSingleObject(hMutex2,INFINITE);
WaitForSingleObject(hMutex3,INFINITE);
list3->DeleteString(0);
str2.Format("%s%d%s","数据个数:",list3->GetCount(),"个");//动态显示个数
buf3data_num->SetWindowText(str2);
str2.Format("%s%d%s"," 空区个数:",buffer3-list3->GetCount(),"个");
buf3emp_num->SetWindowText(str2);
ReleaseMutex(hMutex2); //释放相关信号
ReleaseMutex(hMutex3);
ReleaseSemaphore(SemaphoreEmpty3,1,NULL);
}
return 1;
}
5.5解决的技术难点、经常犯的错误
因为四类线程类似,拥有类似代码,所以当重复时有点变量未改角标,导致用到的变量未声明的错误。

6. 测试时出现过的问题及其解决方法
6.1.问题1
开始按钮开始运行,但是暂停按钮出现问题,停不下开。

解决方法:将进程挂起,利用函数SuspendThread();将相应的线程挂起,增设一个继续按钮,利用函数ResumeThread();继续运行。

6.2.问题2
停止按钮只能停止第一个缓冲的put操作,不能全部停止。

解决方法:利用循环函数将各线程终止
for(;i!=0;i--)
{
DWORD j;
GetExitCodeThread(Threadp[i-1],&j);
::TerminateThread(Threadp[i-1],j);
CloseHandle(Threadp[i-1]);
}
7 软件使用说明
软件界面如图3
图3
7.1 基本功能
可以自己调节BFUUER容量,以及各线程的运行速度,可以控制运行线程的数量,可以随时控制各线程的启动和暂停,在所有线程运行结束后能在汇总数据输出栏输出本次操作的汇总情况。

7.2 需要运行的环境
Windows 7/xp运行环境以及最新的动态链接库。

7.3 操作
单在启动线程前可以设置BUFFER容量,以及各线程的运行速度,点击启动线程按钮后可以选择哪个线程启动,哪个线程暂停,点击结束线程按钮可结束所有可运行的线程,点击
清空按钮可清空所有输出栏中的数据。

8 总结
8.1 课程设计完成情况
8.1.1 已实现部分
(1) 可以随机产生字符数据,由put操作放入Buff1,buffer中容量单位是字符。

(2)提供良好图形界面,显示Buffer的操作过程。

(3) 可以设定各Buffer的容量、PUT、GET、Move操作的个数;
(4) 可以设定PUT、GET、Move操作的速度;
(5) 实时显示每个Buffer中数据的个数和数据的内容,空闲Buffer的空间的个数;
(6)程序运行结束,显示汇总数据:
总的运行时间;
Buffer中数据的个数;
已放入BUFFER的数据个数;
已放已取的数据个数;
平均每个buffer中的数据个数。

8.1.2 未能实现部分
实时显示线程、进程所处于等待(阻塞)状态的个数。

8.2 小组成员分工和成绩自我评定
王乐:能够完成程序代码,分析程序的要求,找到解决的方法,以及能够在网上找到所需要的信息,能够检查运行错误,通过多次检查代码解决错误。

自我评定:A
吴楠:能够完成页面设计,遇到问题查找资料寻求解决办法,对程序提出自己的建议,完成一些代码工作。

自我评定:B
8.3 课程设计收获
通过我们小组成员的不懈努力,我们最终取得了一定的成果,算是对这次课程设计有了一个交代。

在这次课程设计中,我们学到了许多之前没有接触的到的知识,但是我们通过查阅相关书籍,上网搜索资料,找学长以及有过相关学习经历的同学的请教,终于把这些知识弄得明白并应用到程序的设计的当中。

同时通过编写本程序我们对线程的互斥操作过程有
了深刻的理解,同时自己的编码能力得到了锻炼,有了充足的信心面对今后编程过程中遇到的一切困难。

9 参考文献
[1] 陈维新.C++面向对象程序设计教程(第三版).北京.清华大学出版社.2005
[2] 汤小丹等.计算机操作系统(第三版).西安.西安电子科技大学出版社.2007
[3] 百度百科
[4] CSDN网站以及博文。

相关文档
最新文档