实验一 哲学家就餐问题
一、实验目的:
1、熟练使用VC++6.0编译环境,调试并正确运行程序。
2、理解哲学家就餐问题中出现的问题,进而掌握死锁的必要条件。 3、理解源程序中产生和防止死锁的算法,及相关窗口操作。 4、熟悉哲学家就餐问题流程
5、熟悉 PhilosopherThread(LPVOID pVoid)函数,写出其伪代码(写入实验报告)
6、在VC++6.0环境下编译哲学家就餐问题演示程序,考虑其他解决死锁方法(思考题) 课后完成实验报告
二、实验原理:
1、问题描述
有五个哲学家围坐在一圆桌旁,桌有一盘通心粉,每人面前有一只空盘子,每两人之间放一只筷子。每个哲学家的行为是思考,感到饥饿,然后吃通心粉。为了吃通心粉,每个哲学家必须拿到两只筷子,并且每个人只能直接从自己的左边或右边去取筷子。 2、分配方式
方式一(不会进入死锁)
仅当一个哲学家左右两边的筷子都可用时,才允许他拿筷子。这样要么一次占有两只筷子(所有线程需要的资源)进行下一步的吃通心粉,然后释放所有的资源;要么不占用资源,这样就不可能产生死锁了。 方式二(会进入死锁)
当筷子(资源)可用时,先分配左边的筷子,等待一会后再分配右边的筷子,由于这个过程中,左边的筷子一直没有释放,就有可能产生死锁了。 3、程序运行说明
程序运行过程中会弹出一个MessageBox提示操作者操作: 1)第一个对话框用于选择运行模式
a.选择yes 表示采用的是运行的防止死锁的方式,这样的话整个程序可以一直运行下去,不会产生死锁。
b.选择no 表示运行产生死锁的方式会弹出第二个对话框。 2)第二个对话框用于选择运行时,线程运行的时间 a. 选择 res 线程时间比较短,很快就可以死锁
b.选择no 线程时间跟选择yes 时候的时间差不多,产生死锁的时间稍微长一点。
三、实验过程及分析:
1、伪代码:
1) 不发生死锁的方式(要么一下占用两支筷子,要么不占用 ) var mutexleftchopstick,mutexrightchopstick; beging:
resting; waiting;
p(mutexleftchopstick); //先改变左手筷子信号量 p(mutexrightchopstick); //马上改变右手筷子信号量 GetResource(leftchopstick,rightchopstick); eating;
v(mutexleftchopstick); v(mutexrightchopstick); end
2) 发生死锁的方式(一旦可以占用筷子,就马上占用) var mutexleftchopstick,mutexrightchopstick; beging:
resting; waiting;
p(mutexleftchopstick); //改变左手筷子信号量 GetResource(leftchopstick); //获取左手筷子
p(mutexrightchopstick); //改变右手筷子信号量 GetResource(rightchopstick); //获取右手筷子 eating;
v(mutexleftchopstick); v(mutexrightchopstick);
end
2、代码分析:
1)不发生死锁的方式:先确定两只筷子均没被占用才获取筷子,这样就打破了死锁的必要条件。 2)发生死锁的方式:有筷子即占用,看似效率很高,但因为资源有限,且不可抢占,很容易发生死锁。
四、思考题:
其他解决死锁的方案 1、 规定其中一名哲学家先改变右手筷子的信号量,先获取右手筷子。这样,这名
哲学家左手的筷子就有可能空闲出来,成为左边科学家的空闲右手筷子,此一环永远不会发生死锁。
2、 另一个简单的解法是引入一个餐厅服务生,哲学家必须经过他的允许才能拿起餐叉。因为服务生知道哪只餐叉正在使用,所以他能够作出判断避免死锁。为了演示这种解法,假设哲学家依次标号为A至E。如果A和C在吃东西,则有四只餐叉在使用中。B坐在A和C之间,所以两只餐叉都无法使用,而D和E之间有一只空余的餐叉。假设这时D想要吃东西。如果他拿起了第五只餐叉,就有可能发生死锁。相反,如果他征求服务生同意,服务生会让他等待。这样,我们就能保证下次当两把餐叉空余出来时,一定有一位哲学家可以成功的得到一对餐叉,从而避免了死锁。
3、 允许任意的用户(编号P1, ..., Pn)争用任意数量的资源。与迪科斯彻的解法不同的是,这里编号可以是任意的。
对每一对竞争一个资源的哲学家,新拿一个餐叉,给编号较低的哲学家每只餐叉都是“干净的”或者“脏的”。最初,所有的餐叉都是脏的。当一位哲学家要使用资源(也就是要吃东西)时,他必须从与他竞争的邻居那里得到。对每只他当前没有的餐叉,他都发送一个请求。当拥有餐叉的哲学家收到请求时,如果餐叉是干净的,那么他继续留着,否则就擦干净并交出餐叉。当某个哲学家吃东西后,他的餐叉就变脏了。如果另一个哲学家之前请求过其中的餐叉,那他就擦干净并交出餐叉。
4、 规定在拿到左侧的筷子后,先检查右面的筷子是否可用。如果不可用则
先放下左侧筷子,等一段时间再重复整个过程。分析:当出现以下情形,在某一个瞬间,所有的哲学家都同时启动这个算法,拿起左侧的筷子,而看到右侧筷子不可用,又都放下左侧筷子,等一会儿,又同时拿起左侧筷子……如此这样永远重复下去。对于这种情况,所有的程序都在运行,但却无法取得进展,即出现饥饿,所有的哲学家都吃不上饭。
五、实验总结:
本次实验主要是帮助我们提高阅读理解C语言程序的能力,虽然在前面的课堂学习中,老师已经详细解释了这个哲学家就餐问题的算法思路,但课下我们还是要花费很大功夫来吧伪代码看懂。
从这次实验中,了解到了对于不同的分配方式,有的可能会导致死锁的发生,而有的则会避免产生死锁。若从程序的存储效率和运行效率来考虑是话,源程序所给的伪代码已是一种很好的解决方法。但解决死锁的方案有多种,这需要我们在以后的实践中去不断探索,以得到最佳的解决方法。
实验二 阅读者和写入者问题
一、 实验目的:
1、 熟练使用VC++6.0编译环境,调试并正确运行程序。
2、 理解阅读者和写入者中出现的问题,进而掌握信号量的使用。 3、 理解源程序中管理阅读者和写入者权限的算法,及相关窗口操作。 4、 阅读演示程序源代码,熟悉阅读者写入者问题流程; 5、 写出ReaderThread()和WriterThread()函数伪码;
二、 实验原理:
1、问题描述:
有一个公用的数据集,有很多人需要访问,其中一些需要阅读其中的信息,一些需要修改其中的消息。阅读者可以同时访问数据集,而写入者只能互斥的访问数据集,不能与任何的进程一起访问数据区。
2、源程序算法实现调度说明:
程序中,实现读者优先的原则,即当有读者等待时,优先使读者读消息,等到读者阅读完成后再进行写入操作(不可同时读出和写入,但可以同时多用户读出)。当同时又写入和读出等待时,同样先执行读出操作。
3、程序运行说明:
1)本程序主要用于说明阅读者写入者问题中的资源互斥访问的调动策略,并模仿其访问的过程。采用书上的伪码编制而成,实际上采用的是读优先策略。 2)在本程序中用于表现的图形界面说明:
在程序编译运行后会出现中间一个大的圆圈表示公用的资源,上面一排五个矩形表示5个读者,下面的五个矩形表示五个写入者。每个读者和写入者都有3种状态,休息,等待和操作(读入或者写入)分别用黑颜色,绿颜色,红颜色表示休息,等待和操作。一旦操作者获得资源,可以进行读或者写,我们就划一条从操作者中心到资源中心的线,表示开始操作。
三、 实验过程及分析:
1、伪代码:
var mutex,wrt:Semaphore; readcount:integer;
mutex:=wrt:=1; //定义初始信号量 readcount:=0; //0位阅读者 cobegin Readeri:
begin
P(mutex); //创造段,使以下操作不被打断 readcount:=readcount+1; //等待阅读者加一 if readcount=1 then
P(wrt); //禁止写入 V(mutex);
读数据集; P(mutex);
readcount:=readcount-1; //等待阅读者减一 if readcount=0 then
V(wrt); //开放写入 V(mutex); End Writeri:
begin P(wrt);
写数据集; V(wrt); end; coend
2、代码分析:
源程序很好地管理了并行和互斥,利用P/V操作使得过程不被打断(互斥),使用cobegin/coend实现并行。但在实际生活中,写入者(通常是管理员)应具有更高权限,设计程序通常应该是写入者优先。
四、 思考题:
1、程序中使用到了互斥变量和临界资源来实现互斥,这两者在实现机制上有不同吗,若有,是什么不同?
答:互斥变量属于信号量,其值只可以为0或1,主要用于进程间的互斥应用情况,也即“非此即彼”的状况,而临界资源(共享变量)的值可以是整形变量,主要用于进程间相互合作的同步应用情况,其值大于零时表示可用资源数,其值小于零时表示在该信号量上等待的进程数。 2、考虑演示程序是怎样实现在随机调度进程时间、控制进程延迟时间、关闭进程时间的,找出相关函数,变量,并说明。
答:程序是通过运用函数Sleep(P_DELAY)来实现对进程调度、延迟、关闭时间的控制的,其中变量P_DELAY的值为rand()/25*10,其会产生0到1/25*10之间的一个随机数作为随机时间。
五、实验总结:
这次实验是对C语言程序阅读能力的进一步检测,虽然现在已经在课堂以及书本上了解了此程序的算法原理,但因为这次的代码比上次的哲学家就餐问题更长更难以理解,从中找出核心算法还是有困难,又因其反复调用自编函数,没有注释造成了读者阅读上的困难,说明程序员在编写程序时应尽量在一些核心以及难以理解的地方加上注释,以免给读者读代码带来不便。
这里使用的是阅读者优先算法,同时也会有写入者优先算法,但是单独的阅读者优先或者写入者优先算法都不算太完善,难免会有不足之处,而以后在写设计算法的过程中,我们要逐渐学会考虑到对用户的友好性,使阅读者或写入者优先算法交替使用,从而获得最好效果。
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- oldu.cn 版权所有 浙ICP备2024123271号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务