BUAA OO 第二单元总结
第五次作业
UML类图
UML协作图
架构分析
在本次作业中,因为每个人都已经分配了电梯,因此不需要对人进行调度,只需处理电梯的活动即可。我选择了一个输入线程和六个电梯线程来解决问题。将输入线程与所有的电梯线程拥有一个共享对象,即所有人请求的人堆,各个电梯通过从人堆中找到自己的人请求来进行处理。这样的处理方式因为使用了物理锁所以性能上会略差,但是使用量子电梯缓解了一些。电梯的处理完全放在电梯类中,采用look算法,采用了有点作用的量子电梯,成功解决问题。
bug分析
本次作业并没有bug。
第六次作业
UML类图
UML协作图
架构分析
本次作业乘客没有预先分配,并增加了reset的电梯重置命令,因此增加了两个调度类,一个负责对人请求进行分配,另一个对reset请求进行分配。面对人请求格式的改变,我建立了人请求的子类,拥有和之前的人请求类似的格式。电梯对人请求的处理与上次一致。
本次作业我使用了读写锁,减少了电梯对唯一的人堆的访问时造成的大量阻塞。
调度器策略
通过对往届学长的博客的阅读,我知道了均分、随机分配、自由竞争和影子电梯四种做法,因为本次作业电梯在receive前无法移动,以及性能分中对电梯耗电量也做了要求,因此我便选择了劣化版的影子电梯。
劣化版的影子电梯即将人堆深克隆,在比较各个电梯分配的时间,选择其中最短的电梯。同时跳过正在重置的电梯。完全没考虑电量的问题。
bug分析
在这次作业中,出现了对五个电梯同时重置时,会将所有请求全部交给一个没重置的电梯,致使电梯效率极差的问题,我便选择了一种判断,即当在调度时一个电梯已分配人数大于其容量便会wait,不要轮询,否则容易在下一次作业中出现问题。
还有影子电梯的写法出现了错误,没有及时更新电梯内人数的问题,通过多加了参数解决了。
第七次作业
UML类图
UML协作图
架构分析
在本次作业中,面对神奇的分裂行为,我的选择是删掉原来的电梯,创造两个新电梯。
我遇到了一个很有趣的问题,电梯的关闭应当在人调度线程关闭之后,而人调度电梯应当在双轿电梯线程关闭之后,为了能让电梯顺利结束,我进行了对所有双轿电梯线程在处于wait状态后,输入结束后,人调度线程才能关闭并让所有电梯关闭。
因为A、B两个电梯编号一样,所以他们会对一个人请求进行两个电梯的运行,因此我对我上次作业写的人请求的子类进行了状态的添加,状态有A,B,C在人调度线程对人请求进行修饰。
我的普通电梯和reset调度线程与上次一致。
双轿电梯不碰撞
我使用了一个共享对象来控制只能有一个电梯进入交换楼层,并让进入交换楼层的电梯尽快离开。
调度策略
面对影子电梯实现的复杂化,我选择了对影子电梯的再度劣化,通过类似于自由竞争的比谁先接到人,我选择将电梯全部接完人作为结束的条件而不是上次作业的全部送完人的条件。完全没考虑双轿电梯耗电低的问题。
bug分析
我没什么bug,不过在提交前偶然发现了人调度线程可能报错的小概率事件,是因为没有对线程对象加锁导致的,概率蛮小的,感觉不到1%,我想重现还得改代码才行,我别的地方这种bug也不少,但是都很难重现,也最终没有找到。
同步块与锁
在三次作业中,我在共享对象的读写位置增加了锁,第一次是物理锁,后两次使用的是读写锁。避免了对同一对象的同时读写和写写所遇到的问题。
debug方法
面对多线程debug的方式,使用断点一方面会导致输出结果不一致,另一方面会让时间戳出现问题,因此我会在一些地方增加输出来观察中间量的值或者是否跳出循环的问题,在特殊情况下,我会更改代码来使得小概率出现的bug出现概率增加并在最后该回去。
内聚耦合分析
人调度线程和其辅助类复杂度最高,因为无论是结束线程还是调度策略都十分复杂,因此复杂度很高。输入线程因为要进行输入的关闭和对所有线程的各类型输入发送,因此复杂度也比较高,reset调度线程类也是对reset的分配而导致复杂度较高。
心得体会
这三次作业,让我对于多线程有了非常多的理解,让我从对多线程一无所知变为能够写出实现简单要求的多线程代码,让我对于代码有了更多的兴趣。另一方面,我的代码风格和写代码的能力也有了不少提升,对于面向对象的对象有了更多的理解的感悟。遗憾的是时间太短来不及进行代码的优化,代码性能不是很好,也没有实现一次真正的影子电梯。同时,我还知道了一种叫做双轿电梯的电梯。