OO 第二单元总结

一、总述

在本单元中我们主要学习的是多线程的调度,了解并熟悉如何实现线程安全的问题,通过对电梯调度的三次迭代开发进一步了解多线程的使用

UML类图如下:

UML协作图如下:

二、作业分析

1. 第五次作业

(1)作业要求

   第五次作业主要要求的的是五栋楼各有一部电梯,电梯直接互不干扰,仅共用一个总输入请求,并限制ASL捎带策略做为基准时间要求,总体较为基础

(2)具体实现

本次作业我使用了三种线程:输入线程和调度器线程以及电梯线程,每栋楼一个调度器线程和一个电梯线程,因此总共是11个线程

  • LiftSystem为程序主类,用来启动输入线程(LiftInput)并为每个电梯创建调度器线程和对应的电梯线程,用的是官方包的PersonRequest做为输入包,对输出包并未进行封装加锁,为图方便仅是在每个输出语句上单独加锁。

  • 电梯运行策略我起初策略是每次上升或者下降一个楼层便查询当前楼层是否有进入请求,有请求(且加入后不超过电梯容量)便将当前请求加入电梯中,电梯运行采用Look算法,即每次运行至当前方向有请求的最远楼层

    • 采用的是有请求便加入并未要求乘客目的方向与电梯运行方向一致,因此效率降低,导致后续进入bug修复

    • 在一个方向运行至有请求的最远楼层(包括请求进入和电梯里的请求出电梯)后即换方向运行。

  • 线程安全方面,我将每个电梯的调度器都作为共享对象(主要是考虑主输入LiftInput给各个电梯调度器分配对象和调度器分配出对象给楼层和电梯),安全起见该类中方法全部加锁,确保了线程的安全。本次作业共享对象较少,因此途中遇到的线程安全问题并不多,因为第五次作业仅有一部电梯访问,因此电梯不需要加锁,每个楼层的等待进入队列也不需要加锁。因此每个线程运行需要的锁仅有各个电梯对应的调度器这一个锁,不会产生死锁。

  • bug修复:本次作业bug修复比较坑爹,主要有三点。第一是由于初始楼层设置错误设置为0层,导致每个输出都多出一个到达第一层的输出(被hack率百分百,你们的良心不会痛嘛- -)。第二是由于对输出语句未加锁导致输出竞争,部分时间戳不正确。第三就是我采用的电梯运行策略是每到一个楼层便加入乘客而没有考虑乘客的运行方向与当前电梯运行方向是否一致,这在电梯容量仅有6的时候,很容易被塞满,浪费了电梯容量,导致运行效率低,修复之后将每个楼层的进入请求队列分为上行和下行两种,提高了效率。

    (3)代码复杂度分析

    Class OCavg OCmax WMC  
    Disp 5 11 13  
    Elevator 4.46 7 36  
    LiftInput 2 3 4  
    LiftSystem 2 2 2  
    PersonMSG 1.29 3 9  
     Build  2  3  2  

    由表格可见,电梯调度相关算法的复杂度较高,调度器线程和电梯线程的run方法复杂度也较高(可能是因为while(true)???)

    (4)bug分析 & hack策略

    本次作业中初始楼层设置错误,没有封装输出安全,一开始运行策略不好效率也没那么高,但强测10个点居然都过了。。。,但在互测中被hack率高达百分百。由于输出竞争安全问题也hack到了其他人。

  • 2. 第六次作业

    (1)作业要求

    本次作业要求在第五次作业的基础上增加了横向的电梯以及电梯动态增加的问题

    (2)具体实现

    基于第一次作业,大概提出了这样几种构想:

    1. 固定分配:

    • 比如一栋楼有五部电梯,那将进入的输入固定给那部电梯,如1,6,11固定给第一部电梯,这样分配相当简单代码改动少,但是显然这种简单的分配策略效率比较低下无法达到时间基准,后续在研讨课的时候听同学讲的统一分配大概也是预先分配好,但采用的分配策略更合理贴合电梯运行(比如分配给当前时间点最近的电梯),因此效率也高

    1. 自由竞争:

    • 助教提供的想法多部电梯抢乘客,谁先抢到乘客就是谁的。

    • 由于现在有多部电梯间共享队列(一开始并没有注意到这个问题lol),因此要注意线程问题,每个电梯访问时要注意加锁,但是由于其他电梯的访问,可能不能实时的获得锁,因此可能对性能有一定影响,同时陪跑也会占用输出时间。

    但统一调度胜在线程安全,也更合乎常理,此外,自由竞争时的陪跑可能要输出多余的arrive信息,也会消耗一定时间,因此一种较好的统一调度方式或许会有更优的性能,一开始我并没有多想因此直接采用助教提供的自由竞争的方式。

    本次纵向电梯改成了纯正的look,横向电梯类似look写了移动策略,只是要注意横向电梯是一个循环的方向这个问题。

    关于线程安全方面,本次采取了自由竞争的调度方式,同座(层)电梯间共享请求队列,因此需要特别注意读写的互斥访问。在需要套锁时套好锁,确保线程的安全。

  • (3)Bug分析

  • 本次作业可以说是非常惨,由于没有使用yield和适当的sleep之类的,导致了不好的notify,我轮询了,甚至没有进互测。我深刻的意识到了线程安全的重要性。后续也想到了一种应对策略就是多在可能产生轮询问题的地方加入输出语句,这样在观察到大量输出的时候便可以知道是发生了轮询

    三、单元总结

       在本单元中,我们通过电梯调度的三次迭代开发,深入的了解多线程开发的过程和可能遇到的线程安全问题。总体来说收获是巨大的,一开始甚至连什么是多线程都不清楚,到之后学会怎么合理的加锁提高效率且不会产生死锁,如何避免轮询的发生等问题。当然仅仅是这种生产者消费者模式的几次练习,对于我完全掌握多线程的开发是完全不够的,不仅是课下作业以及上课实践的练习,课下自己也还是应该多多练习,以求更加熟练的掌握多线程开发的过程。另外还有一件事就是初步和操作系统的课程建立起了练习,学会了管道的一些运用方式。总而言之这单元的内容相对于以往的代码练习更加新颖,同时也是一种全新的有趣的挑战。

posted @ 2022-04-29 15:23  zzb不是bzz  阅读(4)  评论(0编辑  收藏  举报