一.课程设计题目某银行提供10个服务窗口(7个对私服务窗口,3个对公服务窗口)和100个供顾客等待的座位。
顾客到达银行时,若有空座位,则到取号机上领取一个号,等待叫号。
取号机每次仅允许一位顾客使用,有对公和对私两类号,美味顾客只能选取其中一个。
当营业员空闲时,通过叫号选取一位顾客,并为其服务。
请用P、V操作写出进程的同步算法。
二.课程设计目的1、掌握基本的同步与互斥算法,理解银行排队系统操作模型。
2、学习使用Windows 2000/XP中基本的同步对象,掌握相关API 的使用方法。
3、了解Windows 2000/XP中多线程的并发执行机制,实现进程的同步与互斥。
三.课程设计要求◆学习并理解生产者/消费者模型及其同步/互斥规则;◆学习了解Windows同步对象及其特性;◆熟悉实验环境,掌握相关API的使用方法;◆设计程序,实现生产者/消费者进程(线程)的同步与互斥;◆提交实验报告。
四.需要了解的知识1.同步对象同步对象是指Windows中用于实现同步与互斥的实体,包括信号量(Semaphore)、互斥量(Mutex)、临界区(Critical Section)和事件(Events)等。
本实验中使用到信号量、互斥量和临界区三个同步对象。
2.同步对象的使用步骤:◆创建/初始化同步对象。
◆请求同步对象,进入临界区(互斥量上锁)。
◆释放同步对象(互斥量解锁)。
五.需要用到的API函数及相关函数我们利用Windows SDK提供的API编程实现实验题目要求,而VC中包含有Windows SDK的所有工具和定义。
要使用这些API,需要包含堆这些函数进行说明的SDK头文件——最常见的是Windows.h(特殊的API调用还需要包含其他头文件)。
本实验使用到的API的功能和使用方法简单介绍1、WaitForSingleObject( hSemaphoreChairs , INFINITE );WaitForSingleObject( hMutex , INFINITE );●功能——使程序处于等待状态,直到信号量hHandle出现(即其值大于等于1)或超过规定的等待时间●格式DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds);●参数说明hHandle——信号量指针。
dwMilliseconds——等待的最长时间(INFINITE为无限等待)。
2、ReleaseMutex( hMutex );●功能——打开互斥锁,即把互斥量加1。
成功调用则返回0●格式BOOL ReleaseMutex(HANDLE hMutex);ReleaseSemaphore( hSemaphoreShoppers ,1,NULL);●功能——对指定信号量加上一个指定大小的量。
成功执行则返回非0值●格式BOOL ReleaseSemaphore(HANDLE hSemaphore,LONG lReleaseCount,LPLONG lppreviousCount );●参数说明hSemaphore——信号量指针。
lReleaseCount——信号量的增量。
lppreviousCount——保存信号量当前值。
3、hShoppersThread = CreateThread ( NULL ,0 , fnTreadFunction ,NULL , 0 ,NULL );CreateThread●功能——创建一个在调用进程的地址空间中执行的线程●格式HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParamiter,DWORD dwCreationFlags,Lpdword lpThread );●参数说明lpThreadAttributes——指向一个LPSECURITY_ATTRIBUTES(新线程的安全性描述符)。
dwStackSize——定义原始堆栈大小。
lpStartAddress——指向使用LPTHRAED_START_ROUTINE类型定义的函数。
lpParamiter——定义一个给进程传递参数的指针。
dwCreationFlags——定义控制线程创建的附加标志。
lpThread——保存线程标志符(32位)4、hMutex = CreateMutex ( NULL , FALSE , NULL );hMutexBarber = CreateMutex ( NULL , FALSE , NULL );●功能——创建一个命名或匿名的互斥量对象●格式HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,BOOL bInitialOwner,LPCTSTR lpName);5. hSemaphoreChairs = CreateSemaphore ( NULL ,dwWaitVolume , dwWaitVolume , NULL );hSemaphoreShoppers = CreateSemaphore ( NULL ,0 , dwWaitVolume , NULL );●功能——创建一个命名或匿名的信号量对象●格式HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,LONG lInitialCount,LONG lMaximumCount,LPCTSTR lpName );●参数说明lpSemaphoreAttributes——必须取值NULL。
lInitialCount——信号量的初始值。
该值大于0,但小于lMaximumCount指定的最大值。
lMaximumCount——信号量的最大值。
lpName——信号量名称。
hBarberThread = CreateThread ( NULL ,0 ,fnBarberFunction ,NULL , 0 ,NULL );●功能——创建一个在调用进程的地址空间中执行的线程●格式HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParamiter,DWORD dwCreationFlags,Lpdword lpThread );●参数说明lpThreadAttributes——指向一个LPSECURITY_ATTRIBUTES(新线程的安全性描述符)。
dwStackSize——定义原始堆栈大小。
lpStartAddress——指向使用LPTHRAED_START_ROUTINE类型定义的函数。
lpParamiter——定义一个给进程传递参数的指针。
dwCreationFlags——定义控制线程创建的附加标志。
lpThread——保存线程标志符(32位)六.原理及算法1、信号量设置hMutex:取号机互斥信号量hSemaphorePubCus:等待对公服务顾客的数量hSemaphorePriCus:等待对私服务顾客的数量hSemaphoreSeats:剩余空座位的数量2、线程创建fnPubser1,fnPubser2,fnPubser3:3个对公窗口线程fnPriser1,fnPriser2,fnPriser3,fnPriser4,fnPriser5,fnPriser6,fnPriser7:7个对私窗口线程fnPubTreadFunction:对公顾客线程fnPriTreadFunction:对私顾客线程seat :可用座位数量Pubcus=0:初始对公顾客等待数量Pricus=0:初始对私顾客等待数量dwCustoms=0:初始顾客排队数量3、P、V操作semaphorehMutex,hSemaphorePubCus,hSemaphorePriCus,hSemaphoreSeats;int seat,Pubcus=0,Pricus=0,dwCustoms=0;hMutex.value=1;hSemaphorePubCus.value=0;hSemaphorePriCus.value=0; hSemaphoreSeats.value=seat;process A//顾客线程{ int i=0;p(&hSemaphoreSeats);p(&hMutex);在取号机上取号;int set=rand()%2;Switch(set){case 0:创建对公顾客线程;dwCustoms++;default: 创建对私顾客线程;dwCustoms++;}v(&mutex);等待叫号;接受服务;v(&hSemaphorePubCus); //当取到对私服务号时为v(&hSemaphorePriCus); }process B//窗口线程,分对公窗口与对私窗口,执行过程相似,在此只写出其中一个窗口线程{P(&hSemaphorePubCus);//当取到对私服务号是为p(&hSemaphorePriCus);若有顾客等待,则通过叫号为下一位顾客服务;dwCustoms--;v(&hSemaphoreSeats);为顾客提供服务顾客离开;}七.算法流程图开始客户到达选择服务窗口窗口忙?排队服务并离开队列空?队头取客户窗口闲置处理并离开时间到?结束八.主要数据结构及实现通过创建十二个线程来实现银行排队系统,3个对公窗口,7个对私窗口,1个对公等待顾客,1个对私等待顾客,十个窗口建立之后,来顾客就执行,没顾客就挂起,进入等待状态,通过设计一个随机数来实现对公对私窗口的区分,服务时间可以通过设计一个随机时间来实现,四个信号量,其中一个互斥信号量是取号机的,因为取号机只能一个人用,其余三个分别是等待室的信号量,对公和对私服务信号量,进来一个人时,先检查座位是否满了,没满,则取号,进入等待室,然后等待窗口叫号,当服务完时,离开并释放一个座位。
顾客线程创建过程:(顾客线程分对公顾客线程与对私顾客线程,创建过程基本类似,下面列举对公顾客线程创建过程)DWORD WINAPI fnPriTreadFunction(LPVOID lpParameter){/*进入等待室PV操作*/WaitForSingleObject( hSemaphoreSeats , INFINITE );//检查等待室有没有空位,有则继续WaitForSingleObject( hMutex , INFINITE );//进入等待室,同时不允许其他顾客进入PrivateCustomers++;cout<<"\n第"<<PrivateCustomers<<"位对私顾客进入!\n";ReleaseMutex( hMutex );ReleaseSemaphore( hSemaphorePrivateCustomers,1,NULL);//释放一个信号量使顾客可以接受服务return 0 ;}窗口线程创建过程:(窗口线程分3个对公窗口与7个对私窗口,创建过程基本类似,下面列举对公窗口1线程创建过程)DWORD WINAPI PublicSevice2(LPVOID lpParameter){while(1){if (PublicCustomers <= 2){cout<<"对公窗口2空闲!\n";}/*开始对公服务PV操作*/WaitForSingleObject( hSemaphorePublicCustomers , INFINITE );//检查有没有顾客等待服务,有则继续WaitForSingleObject( hMutex , INFINITE );//进入等待室时不允许其他顾客进入WaitForSingleObject(PublicSevice2 , INFINITE );//开始服务ReleaseMutex( hMutex );//释放互斥量,室其他人可以进入ReleaseSemaphore( hSemaphoreSeats ,1,NULL);//释放信号量使其它顾客可以进入等待室cout<<"第"<<PublicCustomers<<"位对公顾客正在服务!\n";//窗口正在服务,服务延时cout<<"第"<<PublicCustomers<<"位对公顾客离开!\n";WaitCustoms--;//记录顾客离开的序号ReleaseMutex( hSemaphoreSeats );//释放信号量使其它顾客可以到达窗口接受服务CloseHandle(PublicSevice2);}}九.实验测试结果及结果分析结果分析顾客进入银行之后,首先判断是否有空座位,若有,则在取号机上取号,等待窗口服务。