OO第二单元作业总结
OO第二单元单元总结
一、架构设计
第一次迭代
首先,我先在主程序里创建了一个调度中心进程,一个等待序列和一个输入进程。然后我将等待序列作为一个属性传进了调度中心和输入进程中,主要目的为了实现请求的读入和当输入结束的发出信号结束调度中心的运行。
然后我在设计了一个电梯控制器,他本身也是个独立的进程,管理着一台只能实现基本操作的电梯,包含一个需求序列和三中面对不同模式的调度方法把电梯控制器作为一个属性传入到调度中心中,整个结构就算是完成了。

第二次迭代
在第一次迭代的基础上,我加入了一个ElevatorQueue统领所有电梯,并将ControlCenter传入到InputThread中,以实现一读入Add Elevator的请求就加新电梯到ElevatorQueue当中,更快实现调度。

第三次迭代
第三次迭代与第二次变化不大,仅仅是将电梯与waitQueue联系了起来,以实现换乘后新请求可加入到InputThread当中。其中Competition为一个抽象类,存储了多部电梯竞争同一个请求的方法,而Method类中则存着电梯应对不同模式的运送方法(两者在第一次作业中均已实现)。

UML协作图

二、同步块设置和锁的选择
第一次迭代
在每次调用waitQueue实现加入请求,读取请求时,我会用sychronized为waitQueue加锁,同步块设置于加入和读取请求处。由于每一台电梯都有他自身的控制器(ElevaotorControl),在从调度中心读取请求后,会将请求存在Requests这个数组中,然后在合适的时机(即准备进入电梯时)将请求加入到Elevator的Capacity数组中,因此,每次移动时,我都会为Requests数组加锁。在输入进程结束时,会锁住waitQueue,notify调度中心,告诉调度中心输入已结束。
第二次迭代
由于增加了电梯队列(ElevatorQueue),因此,新增在增加新电梯以及选择将请求分给哪个电梯时,将ElevatorQueue上锁,在调度中心得到InputThread已经结束时,锁住ElevatorQueue,给ElevatorQueue信号ControlCenter已结束。
第三次迭代
新增在电梯输出换乘人员的新请求时,为waitQueue上锁,同时由于waitQueue会接受换乘请求,因此ControlCenter的结束不仅需要InputThread的结束,还需要确保的ControlCenter不能接受新的换乘请求(判断方法:锁住ElevatorQueue,逐个判断电梯中是否有人和其需求队列是否为空,如果都为空,则可结束ControlCenter的运行,如果不是,则ElevatorQueue等待waitQueue,当有电梯停止时,则执行waitQueue.notifyall(),唤醒ControlCenter,再重复以上方法。
三、调度器设置
三次迭代的调度器设置变化不大。调度器有两种:一种是调度中心的调度器,一种是每台电梯自身的调度器。
首先,根据不同模式从waitQueue读入不同数量的请求(Night模式当InputThread结束后一次性全部读入,剩余模式一有请求就处理)。调度中心(ControlCenter)读取ElevatorQueue中各个电梯的状态把请求分给各个电梯(优先级:是否空闲>离请求的远近,第三次迭代:是否可到达请求所在楼层>是否空闲>离请求的远近),这就是调度中心调度器的工作。当电梯控制器得到调度中心的请求时,会放在电梯控制器的Requests的数组里,电梯自身的调度器开始工作,确定处理请求的顺序和电梯的运行(Night模式:对请求序列进行排序,到达请求最高的楼层,往下运行开始接人,直到电梯满载。其余模式:以请求序列中第一个请求为主要目标,在到达该请求的出发地以及到该请求的目的地的过程中,通过ALS捎带策略进行捎带,若请求序列为空,则以电梯中第一个进入的人的出发地和目的地为目标,第三次迭代:若进入了B电梯且请求的目的地不可到达,则在目标楼层的低一楼层放下请求,并向waitQueue输出换乘后的请求,若进入C电梯且请求的目的地不可到达,则根据目标楼层在3楼(目标楼层<=13楼)或在18楼(目标楼层>13楼层)放下请求,并向waitQueue输出换乘后的请求。
四、程序的bug
三次迭代中的bug都非常类似,主要分成两类。
1.调度策略不够优秀
在每次强测时都会有一些非常强的数据。如在接近210s时集中输入大量到达同一目的地的数据等,由于ALS捎带策略写的不太正确,所以会出现超时的情况,在小小的修改后即能满足强测要求。
2.死锁
主要在各种进程结束的时候会死锁。
由于第三次是基于第二次和第一次,因此主要讲一讲第三次的情况。
先说一下我的进程结束的条件。InputThread结束时文件读入结束。ControlCenter结束时需要InputThread结束以及所有电梯的请求序列和内部无请求(即不会产生换乘的请求)以及waitQueue为空。ElevatorControl结束需要由Controlcenter的结束以及自身电梯内部无请求和请求序列无请求。因此ControlCenter和ElevatorControl会形成一个闭环,两者互相需要彼此的特定条件。因此,当ElevatorControl在运行还未进入等待waitQueue时,ControlCenter已经通过waitQueue.notifyall()时试图唤醒等待中的ElevatorControl,但由于notifyAll早与wait,所以造成ElevatorControl一直处在等待状态。
3.轮询
在第三次迭代中,ElevatorQueue会在有空电梯时被唤醒,然而由于第三次迭代不是所有请求都可以进入BC电梯,所以每当判断结束ElevatorQueue都被唤醒,导致轮询。为此,我新增了一个变量来判断该请求是否进入过BC类电梯,如果已有,则不会再进入同一类型的电梯。
五、心得体会
我觉得我本次作业的思路比较清晰,并且我努力地实现开闭原则,可扩展性还行。但是在调度策略上略有欠缺,导致运行时间过长。同时,我经历过多次死锁问题,在解决死锁问题上小有经验,但也给了我更多的思考,如果再次解决类似的问题,是否应该建立线程安全类等。总的来说,迭代还比较顺利,没有经历重构,让我感受到了自己的进步。

浙公网安备 33010602011771号