经过三周的自己电梯瞎设计,下次坐电梯想我想的可能就不是如何优化调度算法,而是千万别把自己死锁在电梯里了(手动狗头)
一、设计策略
1. 需求分析:
- 作业一:单部多线程可稍带电梯,一部电梯,固定楼层,不限载人数,性能基准为ALS电梯
- 作业二:多部多线程可捎带电梯,不定电梯数,固定楼层,限制载人数
- 作业三:多部多线程可捎带电梯,动态增加电梯,楼层、运行时间、最大载人数为不同的固定值
2.设计分析:
终于体会到迭代的舒服之处了!!!
此三次作业基本一脉相承,始终坚持生产者-消费者模式要求,坚持WorkerThread模式要求,坚持多线程安全性最先的要求,成功的完成了迭代式的三次电梯作业。
一共四个线程,主线程用来启动输入线程和调度器线程,调度器线程中进行启动多部电梯线程,通过一个共享变量将输入线程和调度器线程联系起来构成生产者-消费者模式,调度器内部多个共享变量分别和各部电梯形成生产者-消费者模式。输入线程只管输入数据并将其放入主队列中,调度器线程只管将需求从主队列中取出并按一定的策略分配进各个电梯的外部需求队列中。电梯线程只管依据既定的稍带策略将外部需求队列中的请求取入内部队列中并完成既定运送。(在第三次作业中,由于有了换乘的存在,所以电梯还需要将out的需换乘的乘客重新放入主队列中,当做新的需求处理)
3. 调度策略
电梯自身的策略:LOOK算法,既简单而且效率还不错。(ps:由于自己第一次作业时魔改了一下LOOK算法,结果发现性能还不如纯LOOK算法性能高orz)
调度器策略:第一次作业,单部电梯,无需在此调度。第二次作业,经过多次魔改调度算法后对比发现,单纯的依据哪部电梯的队列需求最少最优的分配策略,性能更稳定并且更高,(优化点,可以采取手段让电梯自行争抢,而并不提前绑定电梯)。第三次作业,同样经过多次魔改调度之后发现,单纯依据各种类电梯的统一需求队列中的需求最少最优的方案,真的真的真的性能最高,同类型电梯共用一个需求队列,自行争抢,毕竟此次性能值包括了乘客总花费时间和电梯运行时间。
我真的不太明白,每次迭代之初修改调度算法时偷懒用的最少最优的调度,竟然比之后处心积虑修改的调度性能更高。难道这就是传说中的大道至简?
4. 死锁问题
很不幸,三次作业我都出现了死锁问题,但是又很幸运的是,这些问题在提交中测之前都发现了(笑脸.jpg)。原因分析为,我在多线程中采用的是synchronize块在需要时再锁对象的方法,随意经常导致锁的交叉需求问题,进而程序一直相互忙等状态。我采取的方法是,理论分析。列出所有有锁的地方,挨个分析互锁的可能性并进行修改。
5. 第三次作业的可扩展性分析
输入线程部分只需根据输入进行解析扩展即可,调度器线程只需根据电梯的要求扩展变化和代替共享的队列以及修改调度算法即可(调度算法虽未单独抽象为一个类,但是已被简化为一个函数,故修改应该也方便),电梯可根据要求修改固有属性即可。
二、SOLID分析
SPR--单一职责原则:软件组件(函数、类、模块)必须专注于单一的任务(只有单一的职责)
我的输入类,调度类和电梯类都只专注与输入,调度分配和电梯运行三个功能,所以我认为满足了此原则。
OCP--开/闭原则:对扩展开放,对修改关闭
由于调度类中的调度算法并没有抽象出一个类,所以在迭代时难免会对调度类产生修改。以及电梯的属性变化,也会使我直接修改源代码,所以此原则不太符合。
LSP--替换原则:子类型可以替代其对应的基类型
由于电梯都是统一建模通过构造参数来创建新的电梯,故本次作业并未涉及继承部分。
ISP--接口隔离原则:将大而全的接口拆分成特定的客户端接口
作业中并未用到接口。
DIP--依赖倒置原则:抽象不应该依赖于细节,细节应该依赖于抽象
电梯类由于是统一建模,具体细节由统一模型的构造参数决定,所以我觉得一定程序上符合此原则。
三、基于度量分析
第一次作业:
第二次作业
第三次作业:
(由于IDEA更新,metrics插件无法使用,故根据记忆以及手动得出以下分析)
三次作业中的run方法好像复杂度都有点高,应该是方法抽离不完全所致
其中第三次作业中Person类的复杂度尤其高,由于我在Person类中case打表进行决定是否换乘,故复杂度极高(争取以后能用算法解决的,我都能够不打表)
四、分析自己程序的bug
此三次作业中,强测和互测均为出现bug,结果归功于我的评测机和手动分析死锁情况,在中测结束之前找出了所有的问题。(由于第一部分已分析了死锁的问题,故在此不再赘述)
五、互测策略
此次多线程互测相比上一单元的互测来说,评测机要求更高,自己写定点投放程序,自己写逻辑判断程序。(ps:我的第一次评测机竟然连到达0层的bug都没测出来,tcl)
其次,由于过分的相信评测机,所以我基本采用的都是黑盒测试,极少手动构造样例,所以三次互测中基本没有收获(除了最后一次,有同学对X1X2X3以外的电梯名称报错,才找到了一个bug)
最后,多线程的独有特性,即使在自己的评测机上出错,在课程组的评测机上也不一定会错;当然自己评测机不会出错的例子,课程组的评测机上也不一定不会出错(当然我没遇到该种样例hhh)
六、心得体会
多线程的锁真的是一个艺术的东西,多了会把自己锁住,少了就会出现莫名其妙的错误,当然锁自己也是有可能的。多线程安全必须慎之又慎,以及多多动手理论分析一下,没有自己解不开的锁(如果有,那就砸烂换把锁)。
(总结起来就是,多线程的安全就是要么我锁我自己,要么就在玄学的世界里畅游,当然大佬都是掌控一切,恰到好处的放锁)
设计原则部分,由于在作业进行中,课上依次讲解了生产者--消费者模式和WorkerThread模式,所以这三次作业的总的框架展开,细节和具体实现通过自己的想法即可。
不得不说经过这三次作业的训练,我加深对多线程经常出现玄学bug和死锁的安全性问题的理解,以及对生产者--消费者和WorkerThread模式的掌握和熟练。实在是收获颇丰。
以及,自我测试和互测的时候一定要黑盒白盒一起上啊,不能偷懒.jpg。