BUAA-OO-UNIT2-Elevator

第一次作业:

同步块设置与锁的选择:

第一次作业相对比较简单,需要实现一个纵向的电梯调度问题。

我将电梯和输入请求都当作线程进行处理,将请求队列作为中间的交互通道,将请求队列中的增加请求、得到请求等操作加锁。这样就可以实现线程安全的交互。

调度器设置:

第一次作业由于只有直上直下的请求,所以调度相对比较简单。我的调度器只是将输入线程中的请求分配到了对应的电梯等待队列中。

架构模式+UML图:

 

 

我考虑使用的是生产者-消费者模式,这样可以构建一个电梯和请求之间交互的架构。

为了避免电梯在获取请求的时候的轮询,我考虑在楼座请求队列为空的时候将线程sleep,在请求队列新增之后才进行唤醒。

UML图如下:

 

 

 

 遇到的Bug:

第一次作业使用的是ALS策略,但在实现的过程中处理的不是很合理,在去接主请求的时候没有处理捎带,导致时间的消耗过大,强测T了一个点,在分析之后决定改为look策略

实际证明在随机数据的情况下look策略的发挥确实比ALS更加优秀,也更加符合常理。

发现别人的Bug的策略:

第一次作业问题主要集中于锁的设置不得当导致死锁的情况,这种情况在随机数据的情况下应该都能测出来

然后还有线程不安全的情况,例如输出的时候没有使用线程安全的输出,这个确实可在本地测试出问题,但由于多线程的随机性,在评测机上复现比较困难。

第二次作业:

第二次作业相较于第一次作业而言没有什么较大的改动,我在将ALS改为look策略后的时间复杂度也非常优秀,因此这次作业就直接摆了

主要是增加了横向的电梯,但是环形横向电梯和纵向电梯其实本质没有很大的区别。

调度器设置:

除了要将纵向请求扔进纵向电梯请求队列,还需要将横向的请求扔进横向的电梯请求队列。

架构模式+UML图

架构仍然使用的是消费者生产者架构

UML图如下:

 

 相较于上一次来说,新增了横向电梯线程,其他的部分没有很大的变化

遇到的Bug:

发现别人Bug的策略:

随机测试(但是好像没有发现Bug

第三次作业:

 第三次作业在前面两次的基础之上,增加了不同楼座不同楼层之间的请求,因此需要将请求进行处理

同步块设置以及锁的选择:

同步块相较于第二次作业来说几乎没有变动

但对于锁,我最开始将RequestQueue中的所有操作都加锁了,但是这样会导致一个问题:

假设现在有两个电梯线程A\B使用同一个请求队列

A.checkEnd()  ->  notifyAll()  -> B.wakeUp()  ->  B.checkEnd()  -> notifyAll()  -> A.wakeUp()  ->  A.checkEnd()

就进入了这样的一个循环中,直到下次这个电梯出现请求之前都会出现轮询,这就导致了强测一堆轮询被爆杀

所以我在最后删除了getEnd这类判断函数的锁,这样就可以避免轮询的情况出现。

架构模式+UML图:

总体架构还是生产者消费者,但是由于要将请求分段,分段之后的请求有先后顺序,因此加入了流水线架构用来处理分段的请求

在分段的时候,我选择了指导书上静态分配一个横向电梯的方法,这样就可以使得一个请求最多被分成三个部分

分段的部分我新开了一个Change类,目的是为了找到换乘的横向电梯楼层,方便处理

然后在结束的时候,由于请求队列之间可以相互传递请求,终止条件就应该到所有请求都完成而不是输入结束。

在这里我使用了OS中信号量的概念,进行计数,处理完的请求数量在达到输入请求数量的时候才打上end标记

UML类图如下:

 

UML协作图如下:

 

 

 

 

 

 遇到的Bug:

在有多个电梯公用一个请求队列的时候会导致轮询

发现别人Bug的策略:

在测试的时候发现了自己的问题,于是拿hack自己的数据去hack了别人

在寻找轮询的过程中,除了IDEA内部可以查看CPU使用情况,还可以通过输出的方式,如果有大量重复输出的话就说明可能出现了轮询的情况,这种情况也比较方便定位,debug的时候能更快一些。

心得体会:

总体来说这个单元的作业完成的还不错,但由于第三次作业偷懒没有测试导致强测大寄特寄

层次化设计:

学到了很多新的设计模式,在很多地方都能用上,例如流水线、主从模式等等

线程安全:

最开始的偷懒将所有操作都加锁,虽然这样没有正确性的问题,但是时间上表现并不优秀,而且在后期第三次作业也导致了轮询,因此还需要考虑加锁的必要性。

 

posted @ 2022-05-01 10:19  Satom1shihara  阅读(45)  评论(1编辑  收藏  举报