用信号量解决理发店问题.docx
《用信号量解决理发店问题.docx》由会员分享,可在线阅读,更多相关《用信号量解决理发店问题.docx(15页珍藏版)》请在冰豆网上搜索。
用信号量解决理发店问题
一、课程设计的目的与要求………………………………………………………………….3
1、课程设计目的………………………………………………………………………….3
2、课程设计要求………………………………………………………………………….3
二、设计内容………………………………………………………………………………………3
三、开发环境………………………………………………………………………………………3
四、分析设计………………………………………………………………………………………4
1、设计原理…………………………………………………………………………………4
1.1、进程同步………………………………………………………………………………5
1.2、信号量………………………………………………………………………………….6
2、涉及的数据结构………………………………………………………………………..8
3、流程图……………………………………………………………………………………..9
五、运行示例及结果分析………………………………………………………………………11
1、运行示例……………………………………………………………………………………11
2、运行结果分析:
…………………………………………………………………………12
六、个人体会………………………………………………………………………………………13
七、附录(源程序)………………………………………………………………………………15
八、参考文献………………………………………………………………………………………20
一、课程设计的目的与要求
1、课程设计目的
本课程设计的主要目的是通过生产者和消费者问题(理发店问题)的设计和调试,使学生掌握进程同步的工作原理,利用信号量的原理和机制去解决同步问题,并培养学生分析和解决实际问题的能力。
2、课程设计要求
(1)、在Windows系统环境下,用WindowsAPI编程实现操作系统经典同步问题。
(2)、理解和运用信号量、PV原语、进程间的同步互斥关系等基本知识。
(3)、在实习过程中,加强对于进程同步概念的理解,对于实验进行分析。
(4)、掌握在Windows环境下多线程(进程)程序设计的一些基本方法。
二、设计内容
理发店理有一位理发师、一把理发椅和n把供等候理发的顾客坐的椅子。
如果没有顾客,理发师便在理发椅上睡觉。
一个顾客到来时,它必须叫醒理发师。
如果理发师正在理发时又有顾客来到,则如果有空椅子可坐,就坐下来等待,否则就离开。
三、开发环境
操作系统功能的模拟实现可以在计算机系统的终端上进行,也可以在一台微型计算机上进行。
本次课程设计的主要过程是用语言来模拟操作系统的主要功能。
所以,规定试验环境如下:
系统:
Windows2000/WindowsXP
语言:
C/C++
开发工具:
TurboC/VisualC++6.0
四、分析设计
1、设计原理
题目中要求描述理发师和顾客的行为;程序采用用随机数产生顾客进程,也就是顾客按照随机数自动到来,这样更加接近现实生活;当理发师看报时顾客进来需要唤醒理发师为其理发,当有顾客时理发师为其理发,没有的时候理发师看报,因此理发师和顾客之间是同步的关系,由于每次理发师只能为一个人理发,且可供等侯的椅子有限只有n个,即理发师和椅子是临界资源,所以顾客之间是互斥的关系。
程序中引入3个信号量和一个控制变量:
(1)控制变量waiting用来记录等候理发的顾客数,初值均为0;
(2)信号量customers用来记录等候理发的顾客数,并用作阻塞理发师进程,初值为0;
(3)信号量barbers用来记录正在等候顾客的理发师数,并用作阻塞顾客进程,初值为1;
(4)信号量Mutex用于互斥,初值为NULL.
intlongwaiting(0);/*正在等待的顾客的数目*/
intchairs;/*为顾客准备的椅子数*/
customers,barbers,mutex:
semaphore;
customers:
=0;barbers:
=1;waiting:
=0;mutex:
=NULL;
cobegin
barber()
begin
while(TRUE);//理完一人,还有顾客吗?
P(cutomers);//若无顾客,理发师睡眠
P(mutex);//进程互斥
waiting:
=waiting–1;//等候顾客数少一个
V(barbers);//理发师去为一个顾客理发
V(mutex);//开放临界区
cut-hair();//正在理发
end
customer()
begin
P(mutex);//进程互斥
if(waiting)
begin
waiting:
=waiting+1;//等候顾客数加1
V(customers);//必要的话唤醒理发师
V(mutex);//开放临界区
P(barbers);//无理发师,顾客坐着养神
get-haircut();//一个顾客坐下等理/
end
else
V(mutex);//人满了,走吧!
end
coend
1.1、进程同步
进程同步可以理解为(包括进程同步和进程互斥两个方面):
一般来说同步反映了进程之间的协作性质,往往指有几个进程共同完成一个任务时在时间次序上的某种限制,进程相互之间各自的存在及作用,通过交换信息完成通信。
如接力比赛中一组队员使用接力棒等。
进程互斥体现了进程之间对资源的竞争关系,这时进程相互之间不一定清楚其它进程情况,往往指多个任务多个进程间的通讯制约,因而使用更广泛。
如打篮球时双方挣抢篮板球等。
进程是操作系统的核心,进程引进的目的就是为了程序能并发执行,提高资源利用率和系统的吞吐量。
这里并发不等于并行。
并发指:
在一定时间内物理机器上有两个或两个以上的程序同处于开始运行但尚未结束的状态,并且次序不是事先确定的。
在单处理机系统中同时存在多个并发程序,从宏观上看这些程序是同时在执行的。
从微观上讲任何时刻只有一个程序在执行,这些程序在CPU上轮流执行。
并行指严格的同时执行,在多处理机系统中才可能。
并发进程间的关系可以是无关的,也可以是有交往的。
并发进程间无关是指它们是各自独立的,即如果一个进程的执行不影响其他进程的执行,且与其他进程的进展情况无关,不需要特别的控制;并发进程间有交往是指一个进程的执行可能影响其他进程的执行结果,即一个进程的执行依赖其他进程的进展情况。
有交往的并发进程一定共享某些资源。
进程之间互相竞争某一个资源,这种关系就称为进程的互斥,也就是说对于某个系统资源,如果一个进程正在使用,其他的进程就必须等待其用完,不能同时使用。
例如,A,B两个进程共享一台打印机,如果系统已经将打印机分配给了A进程,当B进程需要打印时因得不到打印机而阻塞,只有A进程将打印机释放后,系统才将B进程唤醒,B进程才有可能获得打印机。
并发进程使用共享资源时,除了竞争之外有协作,要利用互通消息的办法来控制执行速度,使相互协作的进程正确工作。
进程之间的相互合作来完成某一任务,把这种关系称为进程的同步。
例如(生产者和消费者)A,B两个进程通过一个缓冲区合作完成一项任务,A进程将数据送入缓冲区后通知B进程缓冲区中有数据,B进程从缓冲区中取走数据再通知A进程缓冲区现为空。
当缓冲区空时,B进程因得不到数据而阻塞,只有当A进程将数据送入缓冲区的时才将B进程唤醒。
反之,当缓冲区满时,A进程不能继续送数据而阻塞,只有当B进程取走数据时才唤醒A进程。
1.2、信号量
信号量的定义:
信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,它负责协调各个线程,以保证它们能够正确、合理的使用公共资源。
Semaphore分为单值和多值两种,前者只能被一个线程获得,后者可以被若干个线程获得。
以一个停车场的运作为例。
简单起见,假设停车场只有三个车位,一开始三个车位都是空的。
这时如果同时来了五辆车,看门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。
这时,有一辆车离开停车场,看门人得知后,打开车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。
在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。
抽象的来讲,信号量的特性如下:
信号量是一个非负整数(车位数),所有通过它的线程/进程(车辆)都会将该整数减一(通过它当然是为了使用资源),当该整数值为零时,所有试图通过它的线程都将处于等待状态。
在信号量上我们定义两种操作:
Wait(等待)和Release(释放)。
当一个线程调用Wait操作时,它要么得到资源然后将信号量减一,要么一直等下去(指放入阻塞队列),直到信号量大于等于一时。
Release(释放)实际上是在信号量上执行加操作,对应于车辆离开停车场,该操作之所以叫做“释放”是因为释放了由信号量守护的资源。
信号量,是可以用来保证两个或多个关键代码段不被并发调用。
在进入一个关键代码段之前,线程必须获取一个信号量;一旦该关键代码段完成了,那么该线程必须释放信号量。
其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。
为了完成这个过程,需要创建一个信号量VI,然后将AcquireSemaphoreVI以及ReleaseSemaphoreVI分别放置在每个关键代码段的首末端。
确认这些信号量VI引用的是初始创建的信号量。
信号量的分类:
整型信号量(integersemaphore):
信号量是整数
记录型信号量(recordsemaphore):
每个信号量s除一个整数值s.valu(计数)外,还有一个进程等待队列s.L,其中是阻塞在该信号量的各个进程的标识
二进制信号量(binarysemaphore):
只允许信号量取0或1值,每个信号量至少须记录两个信息:
信号量的值和等待该信号量的进程队列。
它的类型定义如下:
(用类PASCAL语言表述)
semaphore=record
value:
integer;
queue:
^PCB;
end;
其中PCB是进程控制块,是操作系统为每个进程建立的数据结构。
s.value>=0时,s.queue为空;
s.value<0时,s.value的绝对值为s.queue中等待进程的个数;
1965年,荷兰学者提出了利用信号量机制解决进程同步问题,信号量正式成为有效的进程同步工具,现在信号量机制被广泛的用于单处理机和多处理机系统以及计算机网络中。
信号量S是一个整数,S大于等于零时代表可供并发进程使用的资源实体数,但S小于零时则表示正在等待使用临界区的进程数。
Dijkstra同时提出了对信号量操作的PV原语。
P原语操作的动作是:
(1)S减1;
(2)若S减1后仍大于或等于零,则进程继续执行;
(3)若S减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转进程调度。
V原语操作的动作是:
(1)S加1;
(2)若相加结果大于零,则进程继续执行;
(3)若相加结果小于或等于零,则从该信号的等待队列中唤醒一等待进程,然后再返回原进程继续执行或转进程调度。
PV操作对于每一个进程来说,都只能进行一次,而且必须成对使用。
在PV原语执行期间不允许有中断的发生。
2、涉及的数据结构
程序中引入3个信号量和一个控制变量:
(1)控制变量waiting用来记录等候理发的顾客数,初值均为0;
(2)信号量customers用来记录等候理发的顾客数,并用作阻塞理发师进程,初值为0;
(3)信号量barbers用来记录正在等候顾客的理发师数,并用作阻塞顾客进程,初值为1;
(4)信号量Mutex用于互斥,初值为NULL.
intlongwaiting(0);/*正在等待的顾客的数目*/
intchairs;/*为顾客准备的椅子数*/
customers,barbers,mutex:
semaphore;
customers:
=0;barbers:
=1;waiting:
=0;mutex:
=NULL;
3、流程图
五、运行示例及结果分析
1、运行示例
2、运行结果分析
程序中使用一个链表表示理发店的n个顾客座位,运行结果显示的是理发师理发、顾客随机进入理发店这个过程中,顾客座位的变化情况,即链表的变化情况。
程序运行时间是以通过获取系统时间进行限制的。
到时间下班后理发师就不理发了。
六、个人体会
在21世纪现代科学技术的飞速发展,改变了人类的生活。
作为新世纪的大学生,应当站在时代发展的前列,掌握现代科学技术知识,调整自己的知识结构和能力结构,以适应社会发展的要求。
新世纪需要既有丰富的现代科学知识,能够独立解决面临的任务,充满活力,有创新意识的新型人才,而掌握基础知识就是掌握最基础的能力,所以我们无论学那一门语言都应该把我们所学过的知识灵活运用。
实践是为了使学生进一步消化课堂上学到的理论知识,加深学生对理论工作原理的理解,能够使学生着重掌握实践和理论相结合基本原理和方法,并为以后从事实践技术工作奠定基础。
经过这次操作系统课程上机操作,可以说是苦多于甜,但是可以学的到很多很多的东西,同时不仅可以巩固所学过的知识,而且学到了很多在书本上所没有学到过的知识。
通过这次设计,进一步加深了对操作系统的了解,让我对它有了更加浓厚的兴趣。
特别是当每一个子模块调试成功时,心里特别的开心。
这次的我做的课题内容是用信号量解决理发店问题。
计算机操作系统是现代计算机系统中最核心、最基础的软件。
它是用于管理和控制计算机系统中软、硬件资源,是计算机系统直接与用户打交道的界面,是计算机系统的灵魂和核心。
在计算机学科课程体系中,《计算机操作系统》是一门核心课程,是计算机专业学生必修的专业基础课程之一,也是理解计算机系统工作、用户与计算机系统交互和设计开发应用系统的基本知识结构。
计算机操作系统是理论性很强的一门课程,其内容综合了各种操作系统的结构、设计思想、方法、技术和理论。
现在回顾做课程设计的时候,当看到老师布置给我们的这些题目用信号量解决理发店问题时,我们都蒙了,这么难的题目我们怎么可能会编出来啊,但是埋怨,抗议,一切都是徒劳,我们要尽我们自己最大的努力把程序弄出来,本课程设计的主要目的是通过生产者和消费者问题(理发店问题)的设计和调试,使学生掌握进程同步的工作原理,利用信号量的原理和机制去解决同步问题,并培养我们分析和解决实际问题的能力,刚才开始我们对进程同步的概念根本理不清,什么是信号量,信号量的机制解决问题的原来是什么我们一窍不通,经过了网上资料和书籍的查询终于有了些眉目,当把概念和原理理清了对程序就有一定的了解度。
在设计的过程中遇到问题,可以说得是困难重重,这毕竟第一次做的,难免会遇到过各种各样的问题,同时在设计的过程中发现了自己的不足之处,对以前所学过的知识理解得不够深刻,掌握得不够牢固。
此外我非常感谢老师们给了我们这次的课程设计,它使我更加深刻地体会到多看专业书的重要性,只有掌握了一定量的专业知识才能得心应手地解决诸多问题;另外,做任何事都要有耐心,不要一遇到困难就退缩;在学习和工作中要时刻谨记“团结”二字,它好比通向成功的铺路石,不可或缺。
在这次程序中有过山穷水尽的困惑;有过柳暗花明的惊喜;有过唇枪舌剑的辩论;有过相互鼓励的安慰。
一个多礼拜的时间我们经历了很多,也收获了很多。
与其说它是体力与脑力的作业,不如说它是合作精神和毅力的考验。
经过这次课程设计,我不仅学到了很多知识和技能,更重要的是我们学会了如何运用所学知识去解决实际问题.
做实验同时也可以增加学生的编程能力,动手能力。
综合运用已学知识,理论联系实际,提高学生分析问题、解决问题、实际动手的能力,培养严谨、实事求是的科学态度。
既为后续课程的学习,也为学生今后的学习和工程技术、科学研究工作奠定良好的基础。
通过实验,我们能加深对课程的理解,提高综合应用所学的知识解决实际问题的能力,得出科学研究的前期训练。
通过这次课程设计使我懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的,只有把所学的理论知识与实践相结合起来,从理论中得出结论,才能真正为社会服务,从而提高自己的实际动手能力和独立思考的能力。
七、附录(源程序)
#include
#include
#include
#include
#include
#definerandom(rand()*1000)/RAND_MAX//定义一个随机函数来产生顾客,
//并且使两个顾客间的时间少于1秒
constintQUEUESIZE=7;
constintTimePerMan=1000;
constintWORKTIMELIMIT=10*TimePerMan;
classQueue
{
private:
int*Qlist;
intfront,rear;
intmaxSize;
intcurrLen;
public:
Queue(intsize);
voidpush(intitem);
intpop();
intget();
intsize();//元素个数
boolisEmpty();//判断是否为空
boolisFull();//判断是否为满
};
Queue:
:
Queue(intsize){
maxSize=size;
Qlist=newint[maxSize];
assert(Qlist!
=NULL);
front=rear=currLen=0;
}
voidQueue:
:
push(intitem){
if(isFull()){
printf("Skip!
\n");
}
else{
Qlist[rear]=item;
rear=(rear+1)%maxSize;
currLen++;
}
return;
}
intQueue:
:
pop(){
intitem=-1;
if(isEmpty()){
printf("Empty!
\n");
}
else{
item=Qlist[front];
front=(front+1)%maxSize;
currLen--;
}
returnitem;
}
intQueue:
:
get(){
intitem;
if(isEmpty()){
printf("Empty!
\n");
}
else{
item=Qlist[front];
}
returnitem;
}
intQueue:
:
size(){//元素个数
returncurrLen;
}
boolQueue:
:
isEmpty(){
return(currLen==0);
}
boolQueue:
:
isFull(){//判断是否为满
return(currLen==maxSize);
}
HANDLEMutex=CreateMutex(NULL,FALSE,"Mutex");
//用来实现进程的互斥
HANDLEbarbers=CreateSemaphore(NULL,1,1,"barbers");
//定义信号量来进行线程间的同步
HANDLEcustomers=CreateSemaphore(NULL,0,3,"customers");
longLt0=GetTickCount();
boolrestTime=false;
Queueq(QUEUESIZE);
DWORDWINAPIcustomer(LPVOIDpParm)//顾客的线程
{
while(true)//一直执行
{
WaitForSingleObject(Mutex,INFINITE);
if(GetTickCount()-Lt0if(q.isFull()){//来顾客
printf("队列已满,离开;\n");
}
else{
q.push
(1);
printf("队列人数为%d;\n",q.size());
}
}
ReleaseSemaphore(customers,1,NULL);//v(customer)
ResumeThread(customers);//唤醒理发师线程
ReleaseMutex(Mutex);//释放互斥量,以便其他线程使用
Sleep(random);
}
return0;
}
DWORDWINAPIbarber(LPVOIDpParm)//理发师的线程
{
while(true)//一直执行
{
WaitForSingleObject(customers,INFINITE);//p(customers),等待顾客
WaitForSingleObject(Mutex,INFINITE);//等待互斥量
if(q.isEmpty()){
if(restTime==false){
printf("休息时间;\n");
restTime=true;
}
}
else{
q.pop();
printf("等待人数为%d;\n",q.size());
restTime=false;
}
ReleaseSemaphore(barbers,1,NULL);//释放信号量
ResumeThread(barbers);//唤醒顾客进程
ReleaseMutex(Mutex);//v(mutex);
Sleep(TimePerMan);//理发
}
return0;
}
intmain(intargc,char*argv[])
{
Lt0=GetTickCount();//工作开始时间
HANDLEhThread1=CreateThread(NULL,0,barber,NULL,0,NULL);//产生一个理发师进程
HANDLEhThread2=CreateThread(NULL,0,customer,NULL,0,NULL);
Sleep(WORKTIMELIMIT);
//while(restTime==false){
//;
//}
printf("Exit\n");