第一次电梯
第一次电梯又叫傻瓜电梯(FAFS)。而我也是初次接触多线程,所以对电梯运载人的方式没有多加考虑,仅做傻瓜调度。
即Input线程负责接受Request,并将其直接添加在队列Queue尾。Elevator线程则从Queue取出队头Request,并完成相应的运送和信息输出。
本次作业主要感受和学习多线程,并“回归”靠输出内容进行调试的时代。
第二次电梯
第二次电梯对调度进行了优化,需判断捎带情况以减小电梯运行时间。
本次作业共设置两个队列,分别为总请求队列和电梯内队列。对每个Request增加status属性,用以标志人在电梯内/外和已被运送至目的地。
对电梯增加direction属性用以反应电梯的上下方向。且需注意-1层到1层的特判,防止出现0层。
我的调度器(Controller)依然是一个类而非一个新的线程。调度器主要实现对Request和Elevator的信息判断而确定电梯是否需要开门进/出人。将当前楼层作为参数传入,遍历电梯内队列,判断是否有人出去;遍历等待队列,结合电梯运行方向,判断是否有人要进来。
由于我的调度器中方法写的较少且简略,许多判断操作是在电梯内进行的。这也造成了方法爆行数orz。
如果总请求队列中未处理人数大于电梯内人数或电梯中队列人数大于0,则电梯线程保持醒来状态,否则使其wait,直到有新的请求将其唤醒或因无新输入达到结尾而结束。电梯运行分为电梯中无人和有人两种。无人则取等待队列队首,并依据此设置接到人后电梯的diretion等;有人则在去往目的楼层时,在每层遍历电梯内队列和等待队列,进行对人的操作,直到无人为止。
第三次电梯
第三次电梯共有三部电梯,并需实现换乘。
共设置四个队列,总请求队列sumQueue包涵所有Request,也是其接收Input向其的输入。剩余三个队列为三部电梯的专属队列。共有Input,Elevator和Controller三个线程。
调度器的作用是:从sumQueue不断取出Request,对其进行信息判断后加入到对应电梯的队列中。
ifonly()方法判断其是否只能从单一电梯上;对于固定上某部电梯的Request,onlyifzhida()方法判断该部电梯是否能直达目的楼层;对于不固定上某个电梯的Request,由电梯运行速度可知:如果A能直达选A,再判断B,最后C;非直达则拆分路径。我的做法是先送去一层,生成新的Request,因为一层有三部电梯可选。
电梯仅需遍历自己的队列进行判断和操作。与第二次电梯相比,增加对Request是否进行换乘的判断。需换乘则生成新的Request。
电梯运行还是分为电梯中有人和无人两种情况,与第二次类似。且此次为避免出现和第二次一样爆方法行数的问题,将需多次调用的电梯的move和sleep都拆分成了小方法。
本次作业,对锁和唤醒的要求也较高,我也寻求了大佬的帮助,才得以将其锁清楚。一定要避免暴力轮询和死锁。三条队列要唤醒时,必须唤醒所操作队列,不能一口气全部唤醒。
bug分析
前两次未找到bug,第三次掐死线失败……没来得及提交上评测机。TAT再也不能因为没de完bug一次都不交了TAT
Applying Creational Pattern
这三次作业(尤其是后两次)充分体现了先思考、构架再动手的重要性。且尤其是第三次作业,线程安全是十分重要的,一定要弄清楚何时该锁何时唤醒。