1 作业设计策略分析
1.1 调度策略
三次作业我的调度都是基于ALS算法的,但又不完全是ALS算法,笔者针对每次作业的特性都在ALS算法上有所改进。可以说只是借用了ALS算法中主需求这个思想。
比如在前两次作业中电梯由于没有等待时间的考虑,电梯没人的时候会将离电梯最近的人作为主需求而不是队列中到达最早的人。
1.2 多线程协同与同步控制
-
多线程协同
本次作业我只有两个线程,即输入Input线程和电梯Elevator线程,调度器我并没有作为一个线程,而是作为一个静态的类来使用。本单元的三次作业我都采用了生产者-消费者模式,即Input作为生产者,当有输入进来时就调用Dispatcher类去取。Elevator作为消费者,去Dispatcher中获取Request。
-
同步控制
由于本次作业我的Dispatcher设计的比较简单,Elevator主要调用Dispatcher中的get方法获取需求,Input调取Dispatcher中的put方法传入需求,因此我保险起见直接在这两个方法前面加了synchronized关键字来实现同步。同时为了防止死锁,我并没有用传统的wait和notifyAll这两个函数,而是在电梯循环调用get方法查询时每次sleep 10ms,这样既可以避免死锁,同时还能控制CPUTIME在1s左右避免超时。
2 根据设计原则审视架构
2.1 SOLID原则
-
SRP
即单一职责原则。就我作业中的个各类来看还是比较符合这个原则的。比如Elevator类就只执行人的请求,Input类就只进行输入请求的接收,Dispatcher类只将Request在Elevator和Input之间进行调度。
但就类中的某些方法而言,写的时候没有认真规划考虑,导致部分方法比较冗杂,职责比较多,复杂度等都比较高。
-
OCP
即开闭原则。这单元作业这一原则执行的比较差。本单元三次作业都是迭代进行的,平均来说每次改的并不算太多,为了方便的考量就直接在上一次的基础上对于方法进行修改了。
-
LSP
即里氏替换原则。本次作业中我的Person类继承自PersonRequest类,在其基础上加了一些属性。但没有破坏父类的相关约束,就设计而言遵守了这一原则。
-
ISP
即接口隔离原则。本次作业没有涉及到接口,故暂不讨论这一原则。
-
DIP
即依赖倒置原则。本次作业没有涉及到抽象类,故暂不讨论这一原则。
2.2 性能设计
本次作业我最大的性能设计可能体现在我的Dispatcher并不是一个线程。我的电梯是在每到达一层或空闲时才会访问Dispatcher类获取需求,从而达到局部最优的调度策略,相比于Dispatcher作为一个线程主动分配需求,我认为此种方法可能要更优一些。
3 基于度量分析程序
3.1 UML协作图
3.2 UML类图
通过类图可以看出三次作业的架构基本是一致的,即Elevator和Input两个线程执行请求,再通过Dispatcher类联系这两个线程,说明这一单元作业的架构还不错,能够做到迭代开发不重构。第三次作业为了方便加入了继承自PersonRequest类的Person类以记录更多的属性。各类间也基本满足了低耦合的要求。
3.3 复杂度分析
-
第三次作业
整体来看这单元的三次作业比第一单元要好很多,没有大面积飘红,总体的复杂度不算很高。但是有部分方法飘红,这一部分方法写的时候可能太过于冗杂,只为了功能的实现而忘记了整体程序结构的清晰,在以后作业要多加注意改善。
4 分析自己的bug
本单元作业我吸取了第一单元的教训早早地写了测评机对自己的代码进行测评。但还是出现了一个bug。令我心痛的是由于第一次作业我的初代版本测评机没有加入对Tmax时间的比较,导致某些情况下会大于Tmax的bug没有发现。一方面是由于自己太粗心了,另一方面是写了测评机就侥幸没有重新把代码检查过一遍,这实在是不应该。
这个bug出现在第一次作业的公测中。该bug的原因是在某些情况下会超出Tmax一些从而导致判断超时。事后修改bug时发现仅仅是Elevator类里漏了一句代码导致电梯在某些情况时没有刷新需求,导致总时间会长一些。
5 发现他人bug的策略
本单元互测时我主要通过评测机对他人的代码进行测评。
第一次互测发现了一个人的cpu time超时的bug。
第二次互测没有发现bug。
第三次互测同屋一共8个人,竟然一共成功hack到了5个人,实际上在本地总共发现了6个人的bug,但第6个人的bug提交了两次没有成功。通过强测成绩判断我这次应该是A屋,然而还是发现了如此多的bug,说明许多同学的程序都存在或多或小的问题。6个人中有1个是在某种情况下还有1个需求没有完成就结束了,应该是调度策略的漏洞。其余5个人都是运行时间超过了200s,具体原因应该是死锁问题或者调度策略对于某种情况的优化不是很好。
本单元相比第一单元测评机要难写一些,主要是对结果的正确性判断,需要分各类情况进行逻辑上正确性的证明。
6 心得体会
本单元学习了多线程相关的知识,完成了几次很有意思的电梯作业。总的来说这单元的收获还是挺多的,了解了死锁等相关的知识,并学习了怎么避免死锁,保证线程安全。同时学习了SOILD设计原则,使程序整体结构更加科学、清晰。还学习了生产者消费者、Worker Thread模式等。
我发现本单元在写评测机时对作业的代码也很有帮助,写评测机时要考虑哪些情况判断为错误,因此对每次作业的坑点也算做了一个小小的总结,反过来可以通过代码对照是否避免了这些坑点,这对每次作业的正确性都有不小的帮助。
同时本单元的性能优化也是一大挑战,由于调度策略等比较灵活,本单元在写各次作业时都查了不少的资料,学习了很多知识。