面向对象第二单元总结


单元内容

本单元的内容是电梯的模拟运行,三次作业不断完善电梯的数量和功能,具体如下:

  • 单部多线程可捎带电梯的模拟
  • 多部多线程可捎带调度电梯的模拟(电梯数量是静态的)
  • 多部多线程可捎带调度电梯的模拟(电梯数量是动态增加的)

多线程设计策略

第一次作业

因为只有一个电梯,不需要进行请求的分配,所以除主线程之外只设计了两个线程:请求输入和电梯

没有请求时输入线程可以自行结束,并通知电梯线程,电梯在处理完所有请求后结束


第二次作业

在第一次作业基础上,新增DispatchThread线程,负责将请求分配到各电梯中

请求输入线程可自行结束,并逐级通知 调度线程 和 电梯线程


第三次作业

线程的种类和第二次作业一致,改变了部分线程的产生消亡方式:

  • 电梯数量是动态增加的,所以ElevatorThread线程由RequestInput线程产生

  • 由于存在换乘,DispatchThread线程和ElevatorThread线程相互制约,不能自行结束

    我的设计是:在调度器中判断是否已经处理完所有请求,若满足条件,直接System.exit(0)强制退出


架构分析

第一次作业

采用worker-thread模式RequestInput线程不断地将请求放入调度器Dispatch中,调度器将请求分配给各个ElevatorElevatorThread线程运行电梯

由于只有一个电梯,所谓的 “分配请求” 只是把请求直接扔给电梯


第二次作业

架构没有太大变化,只是因为电梯数量不止一个,新开了一个DispatchThread线程专门进行请求分配

为方便结束条件的判断,我的分配方法是一次性分配当前Dispatch中的所有请求,这对Real time 和 CPU time都是有损失的,然而这个问题我在第三次作业中也没有解决(……)


第三次作业

架构上有两点变化:

  1. 电梯有不同的种类,所以将Elevator类抽象为 抽象类,派生出三种电梯,分别实现各自的换乘方法

  2. 为了实现换乘功能,新增类ElevatorBuffer,记录各电梯扔出的需要换乘的请求,和各电梯的状态(是否将所有请求都处理完毕)

新增缓冲区来储存需要换乘的请求,是为了阻止电梯直接访问调度器,如果两者互相访问,极有可能出现死锁

SOLID设计原则检查

设计原则 反思
SRP-单一功能 做得不好的地方是Elevator类,将电梯调度算法也包括进去了,算法的扩展性和替换性很差
OCP-开闭原则 自认为做得不错,几乎没有修改已有实现,只进行扩展
LSP-里式替换 在电梯上使用了继承,但还做得不够完善,子类的代码相似度很高
ISP-接口隔离 不涉及(并没有新建接口,其实电梯算法这个部分可以用接口实现)
DIP-依赖反转 唯一的抽象部分是电梯,抽象关系很明确,所以符合

bug分析

本单元未出现bug

关于互测,同屋同学的bug真的很少,考虑到互测成本(其实是懒了),并没有修评测机,最终也没有hack到别人(热爱和平)

这单元的bug特点是WA比重不高,印象中三次作业最高的一次也只占到50%左右,A屋的比重还要更少,所以对电梯逻辑的正确性检验并不是重点。如果是做大略的检查,可以让电梯运行的结果以折线图的形式呈现(受sbc学委的启发),这样例如电梯闪现、越界的bug就是一目了然的,也可以看出性能上的缺陷。

除去WA,对RTLE,CTLE的检查,就只需要拿到相应的时间数据,这个是比较容易的。

很多超时问题,都是算法的设计问题,和算法本身的正确性是无关的,这个时候用评测机生成的随机数据反而效果不好,应该用极端数据进行测试,比如大量的20 - -3等等。可惜我也是最后才悟到的。


心得与体会

  1. 关于设计原则

    OCP-开闭原则是一个指导性的原则,在功能不改变的前提下,无需修改已有实现,只需要进行功能的扩展。如果各类不能做到各司其职,而耦合在一起,在扩展时就会牵动筋骨,很难不做修改。在这个原则的push下,自然就会想到要构造各种抽象结构,来增强程序的可扩展性。

  2. 关于线程安全

    要破坏死锁,就要破坏死锁形成的必要条件,其中很重要的一点是不让各线程的资源请求形成一个闭环。可以在局部形成生产者-消费者模式,两个线程对同一个共享对象进行访问,而不是互相访问,从而避免死锁。

  3. 关于性能

    这一单元我的性能分总体较低且起伏很大,分析起来也是比较玄学,但还是有几点可以总结:

    • 首先一点是要充分发挥多线程的优势,不要直接把锁加到一个方法上,最好是可以找到线程安全的最小原子操作,对性能提升很明显。

    • 第二点就是不同人采用的不同的电梯调度策略,用LOOK和SSTF的比较多,我是利用的SSTF算法,前两次比较失败(猜想这里LOOK性能更高一些?),总之都是可以的。

    • 此外就是第三次作业中加入了乘客等待时间这一评价量,为了避免SSTF的“饿死”现象,我记录了各请求投入的初始时间,在空电梯进行请求选择时用 “楼层差 / 等待时间” 作为优先级,性能分有所提升。

posted @ 2020-04-18 15:49  atporter  阅读(171)  评论(0)    收藏  举报