BUAA_OO_unit2 单元小结

李希宸-22371377 学生 2024-04-20 15:51:48

OO_unit2 单元小结

目录

  • OO_unit2 单元小结
  • hw1
  • 架构
  • 时序图(线程协作)
  • 调度策略
  • bug分析和debug策略
  • hw2
  • 架构
  • 时序图(线程协作)
  • 调度策略
  • bug分析
  • bug原因
  • 解决方法
  • hw3
  • 架构
  • 双轿厢
  • 双轿厢不碰撞方法
  • 时序图(线程协作)
  • 调度策略
  • bug分析
  • bug原因
  • 解决方法
  • debug策略
  • 不变与易变
  • 心得体会
  • 感谢

hw1

题目简述:输入会指定不同电梯,完成对应请求。

架构

根据实验上机课代码的架构,搭建该次作业的框架(本单元架构都是基于实验课的架构,不得不说课程组yyds!)。具体而言,根据题目需求设置了InputElevatorRequestRequestQueueSchedulerMain类以及一个枚举类型Status

采用生产者-消费者模型,其中RequestQueue类充当共享缓冲区,Input类代表生产者,不断将请求放入缓冲区,Scheduler类代表消费者,从缓冲区中取出请求,然后分配给对应的elevator的候乘队列中,其中候乘队列也是RequestQueue类型。

  • RequestQueue类:提供生产者-消费者模型中的缓冲区,其中的方法都是synchronized包围的,所以能保证,该类的操作是互斥的。
  • Input类:利用官方包读取请求加入全局可见的请求队列RequestQueue。
  • Scheduler类:从缓冲区取出请求,根据指定的电梯id,分配给对应电梯。
  • Elevator类:从候乘队列取出请求,然后采用LOOK策略进行运行。

img

时序图(线程协作)

img

调度策略

因为输入指定了捎带电梯,所以不需要特定的调度策略。

bug分析和debug策略

该次作业无bug。

hw2

题目简述:输入不指定电梯,同时增加了reset请求。

架构

主体框架仍采用hw1的框架,主要增加调度策略reset请求的处理。

具体而言在Scheduler类里增加一个方法实现调度策略。针对reset请求,考虑对电梯增加一个RESET状态,在Elevator类里新增reset相关操作的方法,从而实现reset请求。

再次感谢课程组实验课代码,yyds again!

img

时序图(线程协作)

img

调度策略

目前主流的策略有均匀分配、随机策略、影子电梯、调参函数(黑盒)等。

起初觉得选一个最简单的方式完成本次作业,后面再进行修改,结果在实现过程中出了一堆bug,后面就没时间实现令人向往的性能之巅——影子电梯(主要是太菜啦)。

实现了两种方法,第一种是均匀分配,第二种,也是后面一直使用的是调参的方式。

均匀分配很简单,就是将请求均匀分配给这6个电梯,如果遇上了电梯reset就跳过,直到顺序分配给能够接受请求的电梯。这种策略考虑的因素较少,“均匀”主要是为了解决请求分布不均,导致某部电梯请求过多拼命干活,其他电梯空闲看戏的情况。但是由于该次作业有reset请求,可以让通过构造特殊样例,让“均匀”也不再均匀。

调参策略,就是给电梯打分,分数越高,越优先接受请求。而打分根据打分函数进行,打分函数考虑各种因素,这里主要考虑了电梯速度,电梯最大容纳量,当前轿厢人数,候乘请求数等因素,其中电梯速度(时间)、当前轿厢人数、候乘请求数与分数成负相关,与电梯最大容纳量成正相关。同样如果电梯正在reset将跳过,直到能有电梯接收该请求。

感觉在调度策略方面,主要考虑的是时间因素,没有特别考虑电量。而调参的方式感觉就是简化版的影子电梯,考虑的参数较少,计算过程更加粗粒度。因为完成作业的时间比较紧,就没有更加仔细设计调参策略,考虑的因素也比较少。

通过后面研讨课的讨论,发现调参也可以做到很好的性能,可以把运行方向请求的运行方向运行多少楼层才能到请求的出发层等等。

bug分析

不幸的是,被刀了。

bug原因

当有5个电梯reset,1个电梯可以接受请求时,所有的请求都会集中在一个电梯里,造成了TLE。

主要原因就是调度策略会直接跳过reset状态的电梯。

解决方法

可以考虑使用缓冲队列(性能更好)。这里我采用的是暴力的sleep方式,当reset的电梯数量大于4时,就强迫sleep(100)。

hw3

题目简述:在之前作业的基础上将reset请求分为两类,引入双轿厢电梯和换乘层。

架构

主要基于上次作业框架增加了双轿厢的机制。

双轿厢

将双轿厢考虑为最低层和最高层不同的电梯。其中主要的问题为电梯在换乘层冲突请求跨越换乘层

  • 电梯冲突:在下面详细解释。
  • 请求跨越换乘层:当电梯到达最高层或最底层就会清空轿厢,然后把这些请求改变一下出发层,重新加入RequestQueue缓冲队列中,让调度器分配给对应电梯。

img

双轿厢不碰撞方法

使用锁,给换乘层上锁。

在这次作业中使用的是ReentrantLock。每个电梯一把锁。当即将达到换乘层时,尝试拿锁,拿到锁后进入换乘层,进行相关操作。

    if (isDoubleEle && curFloor == transferFloor - 1) {
        lock.lock();
    }

同时,换乘层不能久留,规定不会有电梯停在换乘层。离开换乘层前还锁。这样就保证双轿厢不会冲撞,同时不会因为换乘层被久占而发生死锁。

        if (isDoubleEle && curFloor == transferFloor) {
            curFloor--;
            move();
            print("Arrive", 0);
            lock.unlock();
        }

时序图(线程协作)

img

调度策略

与上次作业相同。

bug分析

真正的心寒是强测爆点,互测被刀,腹背受敌。

bug原因

reset accept和reset begin之间arrive的楼层大于2。

主要原因是,未对电梯运行(arrive)进行设置,采用的是优先处理reset请求的方式,保证以最快速度reset begin。但是schedule线程可能被阻塞,elevator线程运行速度过快,导致,未能及时改变elevator的状态(设置为reset)。

解决方法

因为问题主要出现在电梯运行与状态改变速度不匹配导致。

考虑将电梯稍微sleep一下,降低一下速度,从而让电梯状态能够及时改变。

debug策略

主要采用代码走读和输出分析。

感觉多线程的debug不容易复现,代码走读确实是必备的技能,同时也可以考虑采用在代码中设置print断点,通过一些多余的输出来辅助分析代码运行逻辑。

同时每个线程是独立的,如果是6号电梯出现问题,就只用看与6号电梯相关的输出就行。

不变与易变

代码的整体框架还是比较好的,没有任何改变。可扩展性不错。同时代码没有很乱,同步块都集中在RequestQueue类中,几乎所有线程wait和notifyAll都在该类中,死锁概率较小。代码耦合程度不高,比较符合“铁路警察各管一段”。

本单元主要是增量开发,需要根据新增的功能要求,局部修改即可,不需要大改框架。容易改变的内容主要集中在Scheduler类和、Elevator类,因为这两个类完成了最主要的逻辑。所以增量开发带来的改变也主要集中在这两个类里面。

Scheduler类中主要改变的是调度器结束的条件以及调度策略、分配方式。

hw2出现了reset请求,会清空电梯,这是调度器结束的条件不只是输入结束了,同时还有因为reset而需重新分配的请求。
hw3出现了双轿厢电梯,这是调度器结束条件还要考虑无法一次性完成请求,需要换乘的请求。

Elevator类中主要增加请求对应的操作。

hw2出现了reset请求,这里我将其设置为一个RESET状态,增加对应的reset操作。
hw3出现了不同的reset请求,我直接在reset操作里,根据不同reset请求采取不同的reset操作。

心得体会

大名鼎鼎的电梯确实很顶。
这一单元主要是训练多线程面向对象变成能力。看到各种调度策略,各种优化方法,收货很多。量子电梯、影子电梯、调参、随机策略、同步块、读写锁、自旋锁、生产者-消费模型、单例模式、观察者模式、一些很强的容器、volatile关键字等等,还有很多东西需要继续学习。

感觉难度主要是多线程debug以及调度策略的实现。因为时间不够,这个单元还是留有了一些遗憾,属于基本完成了该单元的任务。

同时课程组的讲解,实验课代码的引导,真的帮助了很多orz。没有课程组,不知道这次作业的代码又会是什么样子(肯定混乱无比)。

对于线程安全层次化问题,课程组提供的实验代码已经帮我解决了。整体的结构很清晰就是一个线程进行读入请求,一个线程分派请求,6个线程完成请求。而要进行线程间数据传递时,就设置一个共享缓冲区,该缓冲区是RequestQueue类型实现,做到了抽象与统一,实在是妙。

同时这种架构因为采用的是生产者-消费者模型,共享区全部是RequestQueue这个类的实例,其线程安全问题也被解决。访问共享区的资源也必定是互斥操作,因为RequestQueue中的方法都被synchronized包围。而wait操作也都在RequestQueue类中(出了一个WakeCnt类型),其中的方法对应都实现了notifyAll,所以不会造成死锁。

感谢

感谢课程组老师和助教们,对作业的讲解和答疑,为我们提供了很多帮助。感谢同学们讨论区以及研讨课提出的很好的方法和思想,以及感谢cxc以及ltc、yez等同学提供的很好用的评测机,在debug中帮助了很多。

...全文
34 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

301

社区成员

发帖
与我相关
我的任务
社区描述
2023年北航面向对象设计与构造
学习 高校
社区管理员
  • YannaZhang
  • CajZella
  • C_ecelia
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧