面向对象的程序设计-模块二课程总结

电梯作业的设计策略

在整体的架构上,我三次作业使用的设计策略较为统一,主要的组成部件有:
  • 输入控制线程:用于控制输入,向主等待队列中添加请求(生产者)
  • 主控制线程:用于向电梯分发任务,从主等待队列中拿请求(消费者)
  • 主线程:主线程使用轮询的方式查看任务是否全部执行完毕,并结束程序
  • 电梯线程:用于运行每个电梯的等待队列中的请求
  • 主等待队列WaitList:一个线程安全的共享对象,设置putget方法来放入、拿走请求,并使用wait()notifyAll()来阻塞或唤醒访问该共享对象的线程

从具体的策略上讲,使用了生产者&消费者模型,输入控制线程和主控制线程构成一对生产者和消费者,二者的共享对象是一个等待人员队列,这里我们加锁来保证同一时间只能有一个线程访问该共享对象;

主控制线程向电梯线程分配其执行的人员,每一个电梯具有自己的等待队列,而其任务就是循环执行分配到自己的等待队列中的任务,主控制线程和电梯线程之间的共享对象就是每个电梯的等待人员队列,这里我们同样加锁来保证同一时间只能有一个线程访问等待人员队列(共享对象);

  • 在单部电梯执行任务的策略上,我使用了LOOK方法,即电梯选定一个方向进行执行,在执行完当前方向上的所有任务后,再更换方向执行后续任务;
  • 在人员分配策略上,我将能够直达的请求按照一定的规则分配给对应电梯进行处理,在这里需要考虑的问题是各个电梯的负载均衡和最终的时间最优,在这里我设计了一种根据打分的分配方式,即根据电梯是否已满电梯是否正在执行任务电梯目前所处楼层和方向和当前任务请求的匹配程度来为每个电梯打分,将该请求加入得分最高的电梯中(但是由于测试不够+参数没调,导致运行的效果并不好)。对于不能直达的请求,则会先执行前一部分(运到中转层),由电梯线程放入大的等待队列,再由主控制线程进行新一轮的分配。在中转层的选择上(1或15层),我结合了请求的运行方向分配电梯当前的运行方向请求发出的楼层进行了分配。

  • 程序结束的判定:输入线程终止且电梯运行完毕。在具体的判断上,当输入线程读取到null时,将一个信号量isEnd置位,主线程通过轮询+Sleep来判断
    • isEnd置位
    • 主等待队列为空
    • 每个电梯处于空闲状态且每个电梯的等待队列、运行队列均为空

    在满足上述的三个条件的情况下结束程序

    但是,主线程可能在下述情况下出现错误的判定
    • 最后一个任务从主等待队列中取出,但还没有交给电梯的时候
    • 最后一个任务不能直达,从电梯中出来而未放入主等待队列的时候

    为了避免这样的情况下程序的提前截止,我让主线程在Sleep一小段时间后再次进行判断,只有两次判断均能满足的时候才会结束运行。

基于度量的程序结构分析

SOLID:

SRP单一职责原则

OCP关闭修改、开放扩展

LSP任何父类出现的地方都可以用子类替代

ISP如果不实现全部结构,则为抽象类,不能实例化

  • 第5次作业:单部傻瓜电梯
    • 复杂度分析:

      本次作业中,Main由于需要监控各个线程的运行状况并结束程序,且具有一定的逻辑判断深度,所以造成了其复杂度较高,而类Elevator由于需要维护其两个队列(运行队列和等待队列),并判断和执行相应的任务,导致其复杂度过高。
    • 根据SOLID原则进行分析:

      • SRP:电梯类Elevator需要执行的任务过多,没有很好地满足单一职责原则;

BUG分析及测试

​ 在本阶段作业的公测和互测中没有出现BUG,一方面是由于本次的作业逻辑结构较为清晰,另一方面使用了一些极端样例和大量随机样例进行了测试。对于同组同学的程序,我也没有发现太多的问题。

​ 在测试过程中,我使用了对拍器来检测正确性,也用它来检测电梯的性能,通过统计电梯在每层的负载情况我画出了我的电梯的运行轨迹及负载图(如下):

蓝色: A电梯
绿色: B电梯
红色: C电梯
折线: 电梯运行轨迹
每个方形的大小: 当前电梯内人数
可以看出,在整个电梯运行的过程中,LOOK算法的执行情况较好。但是B电梯的负荷明显过高,而C电梯的任务量太少(运行时间短、运送任务少),电梯整体的运行图能够更加直观地展示在电梯生命周期内的运行负荷,也更易于找出任务分配的不均衡。

在互测阶段,我也为我们组另一位同学画出了这样的运行轨迹图,30S左右输入的40条左右请求,他的电梯运行了160S以上:

从他的电梯运行图中也能发现,他的单部电梯在执行任务的过程中使用的算法不是非常合理,例如B电梯出现了大量”折返跑“的情况;多电梯的任务分配也存在问题,C电梯在程序运行期间有长时间”摸鱼“,A电梯亦有空等时间过长的情况。

心得体会

线程安全

  • 在设计是考虑到线程之间的同步互斥关系
  • 考虑不同线程和共享对象之间的关系
  • 合理上锁
  • 考虑所有情况,避免不安全的情况出现

设计原则

  • 首先保证线程安全
  • 在设计时充分考虑以后出现的情况,OCP原则
  • 设计清晰每个类的职责,遵循单一职责原则
  • 降低类之间的耦合度
  • 使用外部工具来进行正确性分析和性能评价

猜你喜欢

转载自www.cnblogs.com/lebway/p/elevator.html