301
社区成员
发帖
与我相关
我的任务
分享@

hw5的架构中,共享对象较少,只有inputQueue和passengerOut是共享对象,我选择构造一个线程安全的类RequestQueue,将需要保护的读写方法在类中加锁**(synchronized)**,需要使用时直接调用类的方法,不再需要关注线程安全问题。
由于输入中已经规定了电梯号,所以调度器直接根据输入的电梯号来分配即可


第二次作业加入了reset,且需要自己写调度器,现在面临两个问题:
需要,因为有一批因RESET而强制下电梯的人正在被重新调度,他们所位于的楼层一定刚好就是正在RESET的电梯的楼层,此电梯很有可能还是这些乘客的最优电梯
而且,如果直接放弃所有正在RESET的电梯,当五个电梯都在RESET时,乘客会全部被分到一个电梯,性能爆炸。
实现方法,为电梯新增属性stopResetTime,开始reset的时候,将它的值设置为System.currentTimeMills() + 1200,影子电梯计算时间时,多加上stopResetTime -System.currentTimeMills()即可。电梯运行策略不变,修改难度不大
由于正在reset的电梯是不能输出receive的,所以我实现了一个receiveQueue类,当调度器分配请求给电梯的时候,如果电梯处于reset状态,则先不输出revceive,而是将乘客请求先暂存再receiveQueue中,等待电梯reset结束之后,将receiveQueue中的乘客请求统一输出。
这次需要自己写调度策略,我选择了较难实现但是性能高的影子电梯,以下是一些问题和解决方法
将每一条指令当作最后一条,计算分配给六个电梯的结果,看哪一个电梯的综合性能最好
最直接的方法是直接按照课程组给的型能分计算方法,分为三个目标来计算。但是这种方法过于繁琐,且base分未知,只能自行猜测,最终结果并没有想象中的准确,方法性价比不高。我选则将三目标转化为双目标,将等待时间和系统时间合并为时间性能。耗电量单独作为另一个性能,综合考量每一个电梯
假如只考虑时间,那么电梯会尽量吧每个人都尽早送到,很可能出现来了六个请求,而调度器将他们分别分配给了六部电梯,耗电量剧增。
假如只考虑耗电量,电梯会尽量顺路接人,很可能将所有的请求分给同一部电梯,导致等待时间和系统时间非常长。
而且根据课程组型能分的计算方法,当某一项指标过于优秀时,性能分有上限,不会再高,所以我们应当综合的提升各个指标,而不是只专注一一个目标
当一条乘客请求到来时,还是把它当作最后一条请求,分别计算分配给六部电梯后,电梯送完所有乘客所需要的时间t和耗电量w1
再分别计算六部电梯不带这个乘客的耗电量w0
T与目标一**(时间)成正相关,W1-W0**与目标二(耗电量)呈正相关
最终根据公式** score_i = TIME_RATE * t_i + POWER_RATE * (w1_i – w0_i)**
计算每个电梯的评分,取score_i最低的电梯作为目标电梯
影子电梯只能帮我们选择以当前的电梯运行策略为前提下,最好的分配方案,所以电梯的运行策略决定了我们性能的上限,同样至关重要
我采用的是稍加改良版的look算法
传统look算法:电梯的运行方向上没有请求且电梯内为空时转向,接客时只接乘客请求方向与电梯当前方向一致的乘客。
改进版:在接客时,优先接最早到的乘客
电梯超载时能尽量避免某一乘客的超长时间等待


第三次作业加入了双轿厢,共享对象被迫增加,线程数也相应增加,影子电梯的计算方法也需要修改
每组电梯的AB电梯之间存在需要共享的数据,即占位flag,当某电梯运行到换乘层时,flag置1,离开时flag置0
进入换乘层之前需确保flag==0,与RequestQueue类似,建立线程安全的Flag类,实现上述两个功能

难点:乘客可能需要中途换乘,影子电梯模拟时无法预知换乘给哪部电梯
强制规定当乘客分配给双轿厢电梯时,只能由该电梯换乘?
缺少灵活性,且假如A电梯将乘客送至换乘站,B电梯可能需要从远处以空状态运行到换乘层来接乘客,浪费电梯资源
放弃这种方法,改为到达换乘站后乘客强制下车,重新加入inputQueue,重新分配
例如一个1-11层的请求分给换乘层为6的A轿厢电梯,无法一次送达,若仍按之前的方法计算(计算电梯送完所有乘客后所花费的时间和电量),那么非双轿厢电梯一定要比双轿厢电梯所耗费的时间和电量都多。
这里的送完不等于送达,双轿厢电梯在换乘层将乘客赶出来也算送完
解决:将双轿厢电梯计算出的结果加一个补偿,补偿的计算方法如下
假如乘客请求为x-y,双轿厢电梯换乘层为a,将它送到a层后强制下电梯,那么乘客还需要移动y-x层才能到达目的地
在模拟电梯不带人运行时,计算出所有电梯的平均每移动一层开关门的次数times。则可以估算,乘客还需要移动y-x层,期间电梯开关门(y-x)*times次。
根据以上两个估算值,估算出乘客到达目的地还需要的时间和耗电量,作为补偿加在原来的结果上。

为了便于观察每个电梯的运行情况,我用空格和颜色进行了区分,同时在输出类中用flag控制是否打开此功能,提交前将flag置false即可,不需要频繁的注释

检查死锁方面,我在wait()前后,或是synchronized块的前后输出“wait{n}”和“back{n}”,这样在程序卡住时,我只需看一下哪个wait后面没有back,即可定位死锁的位置。


经历了多线程的之后,我意识到写代码前提前构思的重要性,不然debug会很麻烦
层次化见前面的图
最后感谢老师,所有辛勤付出的oo助教们,以及为我提供思路的舍友和朋友。