BUAA_OO_2022第二单元总结

一、作业分析

本单元作业的目标是模拟多线程实时电梯系统

第五次作业

A-E座每层单部电梯,电梯可以在1-10层运行。

1.总体架构设计

本次作业设置了总共4个线程类,分别为主线程,输入线程,调度器线程和电梯线程。由于每座单部电梯,调度器的设计较为简单,无特殊调度策略。电梯调度使用LOOK策略,策略由ElevController中方法生成,具体策略如下:

  • 每当电梯到达一层后,获取下一方向。

  • 当前方向上若有未完成的请求,则不改变方向,否则,改变方向。

  • 运行过程中尽可能捎带。

I. UML类图

 

II.UML协作图

 

2.线程同步处理

主要的共享对象是总调度队列、电梯调度队列和电梯乘客队列。以下是同步处理:

  • 使用继承自LinkedBlockingQueueRequestQueue类实现总调度队列和电梯调度队列,由于其自带的同步访问特性,省略了在puttake操作时同步锁的添加。

  • 电梯人员出入的检测和电梯下一方向的选择操作时,对电梯调度队列和乘客队列加上synchronized同步锁。

  • 输入线程结束时,向主队列添加一特殊请求new PersonRequest(0, 0, '0', '0', 1),调度器线程调度完其他请求并获取到该请求后,退出循环并关闭所有电梯的等待队列,电梯线程在等待队列空、乘客空且队列关闭后结束,程序结束。

  • 创建输出类,将输出套一层同步锁确保输出时间戳同步。

第六次作业

增加横向电梯,支持电梯的增添,请求限制在同座或同层。

1.总体架构设计

本次作业中,为简化线程个数和复杂度,将调度器线程与输入线程合并,调度器类Dispatcher作为普通类,提供一些调度相关的静态方法。在输入线程调用这些方法用于调度请求。另外,增加ElevDispatcher类,提供静态方法用于处理电梯的增添请求。

电梯策略方面,为增加性能并支持横向电梯的LOOK策略,将下一方向的生成操作从每次到达一层进行,改为每次开关门后进行。横向电梯LOOK策略与纵向基本相同,区别如下:

  • 请求方向的判断:若请求位于当前位置顺时针移动1-2位,则判定请求位于顺时针方向,反之亦然。

调度器策略方面:初始考虑使用策略分配,将请求优先分配给距离最近的电梯,但由于实现时bug过多,在第七次作业中更改为随机分配。

I.UML类图

 

II.UML协作图

 

2.线程同步处理

大体的同步处理与第五次作业相同,为适应调度器线程的移除进行如下改动:

  • 输入线程检测到null时,表示所有请求均已调度完毕,此时关闭所有等待队列。

  • 电梯线程在等待队列空、乘客空且队列关闭后结束。

第七次作业

横向电梯增加可达楼座限制,电梯容量和速度可定制,请求可以是不同楼座和不同楼层。

1.总体架构设计

本次作业需要增添一个是否需要换乘的判断。为此,我额外建立了一个ComplexPersonRequest类,继承PersonRequest类,内设属性progresstotal,其中前者表示当前进度,后者表示总步骤数量,total最大为3。

在调度每一条请求时判断若需要换乘,则创建ComplexPersonRequest对象,代替PersonRequest对象进行调度和执行,每当执行完一个步骤后,调用Dispatcher.dispatch()方法,继续寻找合适的电梯进行请求分配。后来反思发现,如果将所有请求均用新类的对象代替,相比我仅仅将需要换乘的请求创建ComplexPersonRequest对象的方法,可以减少许多类型特判,增加鲁棒性,使代码更加清晰。

电梯的运行策略与前一次作业相同,调度横向电梯的选取时,判断若

((switchInfo >> (from - 'A')) & 1) + ((switchInfo >> (to - 'A')) & 1) == 2

则判定为可运送。

若存在多部可运送电梯,由于原先的策略分配设计有误,导致RTLE,故调度策略改为随机分配。

I.UML类图

 

II.UML协作图

 

2.线程同步处理

由于输入线程要反复调用Dispatcher.dispatch()方法来调度不同阶段的请求,在输入结束时调度还未结束。所以在输入结束时输入线程增加了wait()等待,直到电梯处理完最后一个请求后将其唤醒,然后输入线程再关闭等待队列,结束线程。

为实现上述过程,在ComplexPersonRequest类中加入token属性,表示正在处理中的请求数量。由于token是静态属性,token加减的时候需要把整个类加锁。

二、bug分析

1.个人bug

  • 第五次作业强测顺利通过。

  • 第六次作业中,由于缺乏对电梯满员的方向判断,导致部分强测点RTLE。

  • 第七次作业中,由于没有考虑到同层请求也需要换乘,出现了严重bug,导致了强测大量点WA,同时互测也被hack了此处。

以上bug的修复都比较简单,加上对特定条件的判断即可,可见自己的思维严谨性还需加强。

2.发现他人bug的策略

  • 使用随机生成的数据。

  • 构造边界数据,如69.9s时输入,大量同层请求,大量同座请求,超载请求等。

三、心得体会

1.线程安全

本单元的设计过程中,我花了大量的时间在理解和调试线程安全相关的工作。从第五次作业理解掌握同步块的含义到本单元完成,线程安全都是极为重要的一点,如何协调各线程,是主要考虑的问题。相比之下,逻辑实现的难度相对低。在完成了本单元的三次作业后,我对多线程设计的理解变得更加清晰,提升明显。

但由于时间和水平所限,我没能尝试使用课上讲解的更加高性能的读写锁和一些其他设计模式,有点小小的遗憾。

2.层次化设计

OO代码最鲜明的特点就是层次化。本单元虽说没有很多上一单元那样的类之间的继承和实现关系,但在线程各司其职这一方面则更加突出。回想一下自己的设计,在横纵两种电梯的方法实现上使用了一些重复代码,在这一点上层次化的思想还有一定欠缺,本单元作业也成功锻炼了我编写代码的抽象能力和层次化能力。

posted @ 2022-04-30 18:33  LetsHurtTonight  阅读(27)  评论(0编辑  收藏  举报