oo第二单元博客_多线程电梯调度

引言

本单元为多线程电梯,由于此前对多线程一无所知,第一次作业写的很艰难,在第二、三次作业中才逐步理解了多线程。

正文

多线程设计及bug分析

第一次作业

本次作业为单电梯调度,采用LOOK策略。共五个类,其中Elevator为电梯线程,负责电梯运行及调度策略,Input为输入线程,负责获取需求输入,无调度器。

一、同步块的设置和锁的选择

第一次作业中我设置了一个WaitQueue来储存输入线程输入的请求。它不是线程安全类,为输入线程和电梯线程的共享变量,所以使用synchronized关键字对WaitQueue进行加锁,锁住所有对WaitQueue进行读写操作的语句,来保证线程安全。同步块即为所有对WaitQueue进行读写操作的语句。

二、调度器设计与线程交互

本次作业只设计了一个电梯线程和一个输入线程,无调度器。

三、自我bug分析

此次作业在强测中没有找出bug,在互测中被找出了两个bug。

  1. 第一个bug出现在Elevator类的randomMethod()方法。原因为当电梯为空,进行当前运行方向扫描未发现符合要求的请求,进行反向扫描发现请求并前往该请求楼层时,在该楼层只接入了一个乘客,而非将所有符合要求的乘客都接入,最终导致了电梯超时,并导致此次作业几乎没有性能分QAQ。

  2. 第二个bug出现在WaitQueue类的reverseScan()方法。原因为在电梯反向选取离当前楼层最远的需求时,将变量distance初始化为1而不是0,导致在只有当前层有符合要求的需求时,无法找到对应需求,返回了null,导致了空指针异常。

    public PersonRequest reverseScan(int fromFloor, int direct) {
            int distance = 0;//修改bug前为1
            PersonRequest bestChoice = null;
            for (PersonRequest personRequest : personRequestList) {
                if (Math.abs(personRequest.getFromFloor() - fromFloor) >= distance) {
                    distance = personRequest.getFromFloor() - fromFloor;
                    bestChoice = personRequest;
                }
            }
            return bestChoice;
        }
    

四、发现别人bug策略

本单元测试与第一单元相比难度增加了,因为程序运行时间较长且复现率较低。

由于时间问题,仅采用了使用发现自己的bug所用的测试样例进行测试,并未发现bug。

第二次作业

本次作业为多电梯调度,单电梯采用LOOK策略。共七个类,其中Elevator为电梯线程,负责电梯运行及单电梯调度;InputThread为输入线程,负责获取需求输入;Dispatcher为调度器线程,负责进行需求调度。由于增加了需求调度,对第一次作业代码进行了重构。

一、同步块的设置和锁的选择

第二次作业我设置了一个ElevatorQueue和一个WaitQueue进行进程同步和通讯,分别用synchronized对实例化对象进行加锁。

  • ElevatorQueue:线程ElevatorDispatcher的共享对象,用来储存调度器分配给单个电梯的需求。它不是线程安全类,在两个线程同时对其进行读写时会出现错误,所以在两个线程中将所有访问ElevatorQueue的语句设置为同步块,对ElevatorQueue进行保护。
  • WaitQueue:线程InputThreadDispatcher的共享对象,用来储存输入线程输入的所有用户需求。它不是线程安全类,在两个线程同时对其进行读写时会出现错误,所以在两个线程中将所有访问WaitQueue的语句设置为同步块,对WaitQueue进行保护。

二、调度器设计与线程交互

由于本人非常懒惰,只是简单的设计了简单的均分调度模式,即从三部电梯的ElevatorQueue中寻找出最短的队列,并将需求插入到这个队列当中。

DispatcherInputThread之间的交互为生产者-消费者模式,其中Dispatcher为消费者,InputThread为生产者,两者通过WaitQueue这个桌子进行交互。InputThread的职责为向桌子上放需求,Dispatcher的指责为从桌子上取需求。

DispatcherElevator之间的交互为生产者-消费者模式,其中Elevator为消费者,Dispatcher为生产者,两者通过ElevatorQueue这个桌子进行交互。Dispatcher的职责为向桌子上放需求,Elevator的职责为从桌子上取需求。

三、自我bug分析

本次作业强测中被发现1个bug,互测中未被发现bug。

这个bug出现在ElevatorQueuegetReverseRequest()方法。bug出现的原因为单点梯调度算法中,电梯同向扫描未找到符合要求的需求时,进行反向扫描,应该寻找离当前楼层最远的需求。但由于书写错误,导致在寻找时只跳出了1层循环,变为了寻找离当前楼层最近的需求,使得强测中超时了一个点,并损失了很多性能分。

四、发现别人bug策略

本次作业采用测评机来进行随机测试,由于单个测试点运行速度太慢所以采用了python的多进程来进行。但是自己电脑性能太差,多进程大部分点都会被跑丢,有时电脑还会黑屏,所以最后放弃了测试()。测试中也并未发现他人的bug。

第三次作业

本次作业为多电梯调度,且增加了电梯类型要求,可对乘客进行换乘。单电梯采用LOOK策略。共七个类,其中Elevator为电梯线程,负责电梯运行及单电梯调度;InputThread为输入线程,负责获取需求输入;Dispatcher为调度器线程,负责进行需求调度。这一次作业相比于第二次作业改动不大。

一、同步块的设置和锁的选择

第三次作业我设置了一个ElevatorQueue和一个WaitQueue进行进程同步和通讯,分别用synchronized对实例化对象进行加锁,与第二次作业相比没有差别。

  • ElevatorQueue:线程ElevatorDispatcher的共享对象,用来储存调度器分配给单个电梯的需求。它不是线程安全类,在两个线程同时对其进行读写时会出现错误,所以在两个线程中将所有访问ElevatorQueue的语句设置为同步块,对ElevatorQueue进行保护。
  • WaitQueue:线程InputThreadDispatcher的共享对象,用来储存输入线程输入的所有用户需求。它不是线程安全类,在两个线程同时对其进行读写时会出现错误,所以在两个线程中将所有访问WaitQueue的语句设置为同步块,对WaitQueue进行保护。

二、调度器设计与线程交互

调度器对A、B、C三种电梯设置不同的获取需求优先级,C电梯具有最高优先级,其次为B电梯,最后为A电梯。根据它们的优先级和运行限制,调度器对需求进行分配,不进行换乘。调度器与其他线程的交互与第二次作业相同。

DispatcherInputThread之间的交互为生产者-消费者模式,其中Dispatcher为消费者,InputThread为生产者,两者通过WaitQueue这个桌子进行交互。InputThread的职责为向桌子上放需求,Dispatcher的指责为从桌子上取需求。

DispatcherElevator之间的交互为生产者-消费者模式,其中Elevator为消费者,Dispatcher为生产者,两者通过ElevatorQueue这个桌子进行交互。Dispatcher的职责为向桌子上放需求,Elevator的职责为从桌子上取需求。

三、自我bug分析

本次作业强测中被发现1个bug,互测中未被发现bug。
image
这个bug出现在Elevatorrun()方法,为死锁问题。出现的原因如图所示,Elevator线程拥有了ElevatorQueue,想要获取WaitQueueDispatcher线程拥有了WaitQueue,想要获取ElevatorQueue,最终形成了死锁。我的解决方案为将这两个线程获取锁的顺序改为相同顺序,都先获取WaitQueue,再获取ElevatorQueue,从而解决了死锁。

四、发现别人bug策略

由于认为很难找到bug,投入时间成本与回报不成比例,所以放弃了寻找他人bug,最终所在房间也没人发现别人的bug()。

第三次作业架构设计的可扩展性分析

UML类图:
image
UML时序图:
image
我的电梯在多电梯调度策略上并没有很好的可扩展性。从功能设计的角度来看,只在Dispatcher类的run()方法里设置了简单的调度策略,倘若有新的功能需求,只能去大幅修改Dispatcherrun()方法。从性能设计的角度来看,第三次作业其实拥有不错的性能分,强测在挂掉一个点的情况下还拿到了94分,但是要想进行性能提升,依旧要进行大幅度的修改,并没有可扩展性可言。

在单电梯调度上,我将单电梯的调度根据功能分化成了多个方法,尽可能的将每一部分的功能进行最大程度的抽象,其拥有较好的功能性和很好的性能性,可以很轻松的在原有基础上进行扩展。

心得体会

本单元的作业给了我深刻的教训。

首先是要趁早写oo作业,不要拖到周六晚上熬夜写完,这样不仅会导致之后几天精神不振,还会导致代码架构混乱且有很多bug。

其次是不要偷懒,这几次作业基本上都是写完了交上去过了中测就没有管了,导致三次作业都出现了严重的bug,在后续bug修复中浪费了一些时间;而且由于偷懒电梯并没有实现很好的性能,也没有进行优化尝试,这样其实对比于其他同学少学了很多东西,并不是合理的学习方式。

当然在本单元中我接触了全新的多线程的知识,收获也是很大的。

希望自己从下一单元开始可以更认真的写作业吧。

posted @ 2021-04-26 14:00  禾草  Views(58)  Comments(0Edit  收藏  举报