第二单元总结
1、同步块的设置和锁的选择
三次作业公共的有两处同步块。一处为输出类中的同步块,将每部电梯的输出互斥,保证输出时间戳递增。第二处为调度器中的同步块,使得输入向调度器中加入元素和电梯从调度器中获取元素两种行为互斥。其中在get操作时若列表为空则wait,在put操作时notifyAll。
第七次作业在Input类新增一个同步块,使扫描横向电梯列表和向横向电梯列表中加入元素两种操作互斥。
三次作业使用锁的均为synchronized-notifyAll的模式。
2、调度器设计
第五次作业
本次作业采用与现实生活中电梯最为接近的调度策略。具体策略为电梯每行动一次(包括进出人、移动)就更新一次请求,每次将与电梯方向相同且目标点在电梯行动方向的请求中最近的作为当前任务(若电梯已满则忽略进入请求),若没有符合以上条件的请求就将与电梯方向相反且目标点在电梯行动方向的请求中最远的作为当前任务,若以上两种请求均不存在则将电梯掉头重新判断。其中,将每个请求分为两部分,依照其是否在电梯中判断目标地点。
第六次作业
本次作业采用电梯自由竞争的策略。每层及每栋有独立的调度器,电梯在上一次作业的基础上自由选择任务作为当前任务并将其放入自己的任务队列中,当更新任务时若发现更优的任务且当前任务的人并未进入电梯则将该任务从任务队列中退回调度器的任务队列中。
第七次作业
本次作业电梯延续上次自由竞争的策略。对于每个乘客请求,在加入调度器前先为其规划路线(该路线的横向仅考虑直达),电梯依据请求的状态判断其目标地点,并将未送达终点的任务再次发送到Input中。对于横向电梯,在从调度器中获取请求时需先判断可达性。
3、线程协同的架构模式
本次主体架构使用生产者-消费者模式,输入作为生产者,电梯作为消费者。部分类使用单例模式。在第三次作业中,对请求使用流水线架构进行处理。
UML类图及扩展能力
在架构设计时,将电梯的行为(开关门,进出人,移动)和调度器的行为(加入请求,取出最优请求)分离。从第二次作业开始并令横向和竖向的电梯继承电梯类(调度器同样),具有一定的可扩展性。同时,为了便于对PersonRequest类进行操作,我新建了PRequest类继承了该类,并新增了便于规划路线和寻找最优请求的方法。

UML协作图

4、bug分析
第五次作业
共发现2个bug。第一个bug为线程输出顺序可能不递增,解决方案为新增Output类,将所有输出语句加上同步锁。第二个bug为当电梯满载时再从调度器get到一个更近的请求引起的超载问题,解决方法为在get时加入判断,如果电梯为满则仅搜寻已经在电梯里的请求作为主请求。
第六次作业
未发现bug。
第七次作业
共发现1个bug。产生原因为仅在实现任务的拆分时判断了横向电梯的楼栋可达性,拆分后放入相应的横向调度器后仅能保证该层存在能完成此分任务的电梯,但并未保证该层可能存在的其他电梯不会将该分任务取得并执行。因此可能会出现楼栋不可达的电梯将该分任务取得从而违反了楼栋可达性。解决方法为加入判断语句并重写判断请求队列是否为空的函数。
5、寻找bug策略
在寻找bug时,我采用的策略是对每种情况和可能出现的问题都编写一组针对性数据,并对每组数据以三种不同的输入结束时机进行测试,即:在数据输入完毕后立即结束、在程序运行中途结束、在请求处理完毕后结束。这种策略并不具有完备性,全面性依赖于对各种情况考虑的全面程度,但能验证电梯的基本功能实现情况。
对于线程安全相关的问题,可采用在同一时间连续输入针对同一个调度器的数据的方法,并改变数据顺序进行多次测试以提高不可复现bug出现的概率。
相比于第一单元的测试策略,本单元加入多线程后最大的差异是bug不可复现,因此通过了一组数据并不能代表程序对这组数据是正确的,需要进行多次测试才可大致验证其可靠性。此外,本单元的数据增加了时间这一维度,需要考虑的方面极大地扩展了,可以将请求和输入时间进行排列组合以形成更全面的数据。
6、心得体会
本单元作业电梯调度为多线程的首次接触。由于对多线程不甚熟悉及多线程的bug无法复现难以调试,编写代码时在线程安全方面难以理解。但随着理论课学习以及os相关内容的进行,对于线程互斥,等待与唤醒这些操作的使用逐渐变得游刃有余。与此同时,吸取了第一单元的经验,在代码编写时考虑了其可扩展性,使用了父类继承的方式处理具有类似属性的横向和竖向电梯及调度器,并尽量减小其功能上的耦合度,使各部分各司其职,仅在必要的部分联系。
posted on 2022-04-27 20:27 hacker_killer 阅读(42) 评论(0) 收藏 举报
浙公网安备 33010602011771号