OO Unit 2 Summary

第五次作业

多线程设计策略

今年的第五次作业实际是去年的第六次作业,所以虽然是本单元的入门,但门槛还是比较高,既要学习多线程,又要研究电梯调度策略,达到功能(捎带)和性能(时间)的平衡。我采用了比较普遍的架构,即生产者(输入线程)——缓冲区(队列)——消费者(电梯)模式,缓冲区的存、取方法均通过synchronized加锁。

开始时的策略是每次读取队列更新电梯等待队列,然后让电梯运作直到乘客全部完成,再从队列中继续读取,性能很低;但我以为问题出在Look上,就把Look改成了ALS,没有改善。最后把两次读取队列之间的操作变成了移动一层,性能有所改善,但离ddl较近没有来得及改回Look,性能分受到了损失。现在学完整个单元再回顾,临界区过大导致线程并发度低,是性能分低的最本质原因。

度量分析

UML图

类、方法度量

 

可以看出电梯类复杂度较高,这是由于本次作业我将调度器内嵌在了电梯内部,从而导致了比较复杂的业务逻辑,也间接导致了后面作业的重构。

 

自动测评

延续上一单元的传统开发了自动评测,整体部分沿用上一单元,数据随机生成较为简单,稍花了一些时间来写judger判断答案对错,核心部分是通过sleep来实现定时输入。

公测

中测强测均未见bug,但由于使用了ALS以及临界区过大并发度低的问题,性能分较低。

互测

互测也没有出现bug。通过自动测评hack出了Saber和Caster的RTLE(只有一个请求时无法结束,原因是实例化了两次ElevatorInput而没有共享标准输入),但公布结果时发现Caster并没有hack到,反而是hack了Berserker的CTLE(后来发现还是一位熟人,他使用的轮询),共hack成功4次,被hack0次。

心得体会

第一次作业时对于多线程的了解还仅限于课上两节课的内容,最后参考着指导书附带的多线程教程勉强完成了程序,现在回顾来看,在线程交互方面还是有一些可以改进的地方,例如原先为了防止线程安全问题而过于保守地给很多没必要同步的方法加上了锁等等。

电梯类的高复杂度导致了后面的重构,也让我重新反思,现在的设计原则依然受到面向过程的影响,还需要参考其他同学的优秀代码来学习良好的设计思想。

 

第六次作业

多线程设计策略

这次作业实际上是去年第六次与第七次之间的过渡,即单部可捎带电梯、多部动态变化不同型号可换乘电梯之间的过渡:多部固定数量同类型电梯。普遍的做法是每部电梯一个线程,独立运行,调度器将等待队列中乘客根据电梯状态喂给电梯。

但由于这两周时间极其受限(北大双学位期中考试、实验室科研),选择了一种完全牺牲性能但很容易实现、不可能产生线程安全bug的辣鸡架构:所有电梯和manager捆绑在一起作为一个线程,即消费者,输入线程和缓冲区不变。每次manager从队列中读取请求后放至自己的waiting队列,然后遍历所有电梯分别移动一层。也就是说,电梯是傀儡,所有调度均由manager完成。

遍历电梯时如果因为Arrive需要Sleep(open close的sleep不能省略,因为两者之间必须有400ms),则根据本轮遍历是否已经Sleep过决定是否Sleep,即每轮遍历只有第一个电梯需要Sleep,其他电梯如果是Arrive则直接输出即可。不得不说我能想到这种神奇架构真是鬼才。

度量分析

UML图

类、方法度量

本次做的重构在于将调度全部由电梯移动到了manager。由于所有调度均集成在manager,该类十分臃肿,且与电梯有很多数据交互,没有达到合理分工的效果。

自动测评

继续了上一次的自动测评,所幸基本只要修改一点点judger的部分,才得以在有限的时间下完成。通过自动测评找出了一些调度算法上的bug,保证了正确性。

公测

依然没有出现bug,大概是这种神奇架构的好处。但坏处就是性能依旧差(意外的是竟然比上一次高,可能是因为修复了临界区过大的问题)。性能差的主要原因可能是这种架构难以达到请求均分到多个电梯的效果,(想要达到均分需要遍历两轮,非常复杂),只能一次把所有满足Look要求的乘客喂给同一电梯。

互测

依然没有出现bug。通过自动测评hack出了同一个人的两个bug(吃人、超时),由于是线程安全问题所以提交了很多次才hack到,也是整个room唯一提交hack的人。共hack成功3次,被hack0次。

心得体会

选择了保守路线的神奇架构,牺牲了性能换来了正确性和时间,没有去探索优化有些遗憾。如果不是这学期有十门课每天都要学十二个小时的话我一定努力优化

 

第七次作业

多线程设计策略

增加了动态加入电梯、不同类型电梯(容量、运行时间、停靠楼层)以及由此带来的换乘问题,并增加了乘客等待时间作为评价指标。出于依旧忙碌&懒得重构决定延续上一次的神奇架构,因而改动其实相当少。电梯类型问题只需要在构造方法里增加类型判断即可,动态加入也只需要在manager取请求时解析请求类型加入电梯。所以唯一的工作量就是换乘。我采用静态规划,在取出人员请求时调用其换乘规划方法,设定from、to和ultimateTo,若需要换乘则在出电梯时调用其换乘更新方法,使得from=to,to=ultimateTo,然后重新加入请求队列即可。完成迭代只花了两个小时,节约了很多时间。

度量分析

UML图

 

类、方法度量

基本与上一次的度量类似,Person类的换乘分析方法由于是静态分析(23×23种情况,分为多个类型),复杂度很高。

自动测评

延续了上一次的自动测评,只需在judger中增加对不同电梯类型的判断即可。

公测

依然没有出现bug。延续了本单元性能分辣鸡的传统。

互测

在Croom大开杀戒,hack出了5个人共8种不同的bug(死锁、电梯容量、打印调试信息、死循环),事实上最后发现除了我以外7个人均有bug,C房无疑。共hack成功9次,被hack0次。

扩展性分析与SOLID

SRP:类方法的职责明确性。manager的职责较多,取请求、获得电梯状态、调度电梯均由manager完成,职责复杂而不太稳定。

OCP:可扩展性。我的设计虽然聚合度很高,但实际可扩展性还比较好,从第二次到第三次的迭代就可以显示出其扩展性。

LSP:继承替代原则。本次作业没有自定义继承、多态关系,不必讨论LSP问题。

ISP:接口抽象层次。实际上如果将多种电梯定义为同一接口,可以提升一下抽象级别。

DIP:依赖倒置。同上一条所述,如果manager依赖的不是电梯类而是电梯接口,可以更好地达到DIP的目的。

心得体会

由于继承了上一次的架构,整体效果也与上一次类似。没有优化稍有遗憾,也没有很好地实践学习到的设计原则。

本单元整体是有些遗憾的,实际上如果有更多的时间,可以学到更多的多线程和设计原则相关的知识,希望以后有机会补充一下。

原文地址:https://www.cnblogs.com/zkwang/p/12701738.html