OO第二单元总结

OO第二单元总结

一、程序设计架构

总体架构

三次作业总体架构保持一致,在初始架构的基础上进行增量的迭代开发。

第一次作业

需求:模拟多线程实时电梯系统,每座固定一部纵向电梯,处理同楼座移动请求。

代码框架:

 

本架构采用生产者-消费者设计模式,托盘为各楼座等待队列,为每座定义了两条等待队列,根据起点至终点的方向不同分为上行队列与下行队列,简化了电梯方向判定的算法。

代码规模:

 

第二次作业

需求:模拟多线程实时电梯系统,每座初始各一部纵向电梯,可增加纵向电梯与横向电梯,处理同楼座、同楼层移动请求。

代码框架:

 

代码规模:

 

对 TimableOutput 封装为OutputThread类,避免出现输出时间戳非递增的情况

第三次作业

需求:模拟多线程实时电梯系统,每座初始各一部纵向电梯,一层一部横向电梯,可增加纵向电梯与横向电梯,处理同楼座、同楼层移动请求以及换乘请求。

代码框架:

 

代码规模:

 

 

第三次架构与第二次基本相同,主要修改在调度器部分,增加了换乘相关操作的方法。

可扩展性分析

  • 第一次作业中将电梯相关属性保存在电梯类中,后续作业自定义电梯属性时便于修改。

  • 将电梯的每个操作,以及状态判断(是否开门、是否转向等)都抽象出行为方法,逻辑清楚且出现bug时易于修改。

协作图

 

 

二、同步块与锁

同步块

  • 仿照上机练习代码,建立了一个线程安全类 RequestQueue,实现安全增加、修改、取出请求。

  • 封装输出安全类 OutputThread,避免出现输出时间戳非递增的情况。

三次作业中,我都选择了使用关键字 synchronized 修饰的锁机制。

  • RequestQueue中对方法上锁,实现队列修改的线程安全。

  • ElevatorVelevator中对访问的公共队列上锁,防止访问引发线程冲突。

三、调度器设计

在前两次作业中,调度器的核心功能极为简单,即充当“托盘”的作用,接收请求,并分配到候乘表队列中。

第三次作业增添了换乘操作,调度器多了一个“分解”请求的作用,即把换乘请求分解成多个简单请求,这样保证了电梯类只需较少改动,就能完成换乘的要求。

在请求接收方面,采取自由竞争的策略,优点是不需调度器从电梯获取信息,降低耦合性;但有可能会导致电梯接收到不能处理的请求(目标层座不可达),此时需要将请求吐出。

四、DEBUG与测试

第一次作业:

强测出现两个RTLE错误,主要是因为结束时没有判断电梯内是否清空,导致电梯线程没有结束。

第二次作业:

由于未封装输出模块,导致强测出锅,未进互测。

在debug过程中对输出模块进行了封装。

第三次作业:

强测出现两个RTLE错误,一个是纵向电梯中转向函数的缺陷导致,另一个是横向电梯吐出请求的函数,在队列中有多个需要吐出的请求的时候会死循环。

互测无bug。

三次作业中,均采用测评机+构造数据相结合的方式进行测评。

五、心得体会

  • 本单元的设计关键之一在于设计线程安全类,使线程尽可能少的处于临界区之中,保证线程安全;使用锁互斥来解决访问冲突。

  • 工程设计中要坚持SOLID原则,尽量保证类的功能单一,不承担过多的职责,比如调度器只负责分发请求,电梯只负责解决请求等等。

  • 在电梯策略方面,我从始至终使用的都是LOOK策略,虽然性能比较平庸,但代码出锅概率也下降了很多。

  • 通过本单元学习,对多线程、共享资源、线程冲突等概念有了较为全面的的一个认知。有了上个月的经验,虽然电梯单元难度较上单元更大,但是工作进程较上单元顺利很多,没有再出现压着ddl提交的情况,可惜有一次未尽互测,以及自动化测试做的仍不够好,希望下单元能再接在厉。

posted @ 2022-05-03 16:22  陶索梓  阅读(25)  评论(1编辑  收藏  举报