BUAA-2024春-OO-Unit2总结

第五次作业

hw5

如图,根据生产者-消费者模型,总体架构是在InputHandler读入输入请求并分配至每一个Elevator对应的RequestTable,每一个Elevator按照自身的Strategy(采用Look策略)运行。
InputHandler设为线程,是因为输入长时间存在并在时间上不连续,不能等待输入完成再进行处理。但在hw5中,我没有考虑到之后的迭代以及应遵循的单一职责原则,使得InputHandler类耦合度较高,也因此当输入的请求不再指定电梯时,为实现低耦合,后续作业将InputHandler类拆分。
同时,为每一个线程类Elevator设置线程安全类RequestTable,它是分配给该电梯执行的请求。在RequestTable中的请求相当于在该部电梯外等候的队列,而在Elevator内部的destMap中的请求则是在电梯内的队列,二者结合电梯运行的相关信息,从而能够在Strategy类中判断电梯开门,关门,运行,进出的策略,实现运行。
由单一职责原则,对Output单独建类,使得输出模块化。

线程协作

hw5协作图

同步块与锁

对于临界资源,再方法上添加sychronized关键字(锁“this")。在后续作业时,有尝试使用ConcurrentHashMap等线程安全容器,以及更细度的sychronized(obj),和读写锁等,但由于使用不当,自行测试时常会导致阻塞,能力局限探测不出原因,最后出于正确性考量还是回归了sychronize(this)。

调度器设计

hw5指定了每个请求对应的电梯,直接进行分配。
hw6的调度在初始尝试了调参,具体实现是列出四个与电量和等待时长有关的影响因素:电梯与请求所处楼层的距离,电梯的方向,电梯内当前人数与承载量的比例,估算的等待时间。将因素做归一化处理,再进行加权求和,得到一个priority值,同时着重考虑承载要求,若比例小于一定值(设为0.2),则将priority乘一定倍数,依照最终所求的priority值进行排序,取最大值作为分配的电梯。
但考虑到上述调参较为粗糙,电梯每时刻产生的变化难以估计,即使priority最大其结果也难以最优,而对因素的进一步优化确认则会使代码的复杂程度更高;同时各项参数纯为主观臆断,如果需要得到最优客观解,可能需要神经网络等一系列超乎我目前能力的操作。
这些问题使我主观层面难以信任我捏造的公式,于是最终回归均分的基准策略。也即对请求的分配在电梯1到6间循环进行。经过比较,均分的策略在大部分数据中表现更好,因而最终仍采取均分。
hw7延续hw6均分的策略,同时将双轿厢电梯视为一部电梯,只是在运行时有不同的表现。

第六次作业

hw6
相较于hw5,改变的地方有:添加了WaitingQueue类和Schedule类,将调度与输入分离开来,达到解耦合的目的;在requestTable里增添了reset队列,一但reset队列不为空,Elevator将立即抵达最近楼层并reset。
其中,WaitingQueue为线程安全的单例模式,使得reset后Elevator内部的destMap可以将请求再次传入WaitingQueue,以待再次分配(并将这些请求设计为高优先级)。

线程协作

hw6协作图

第七次作业

hw7
相较于hw6,改变的地方有:增添了第二类Reset机制,初始时增加创建的RequestTable的数量,Elevator将在reset操作结束后创建两个新的Elevator子线程,再将二者对应的RequestTable和所需参数传入,启动子线程后结束本线程。
为实现双轿厢的两个轿厢不碰撞,借助了自旋锁的思想,相当于对进入电梯轨道的transfer层设置了一把钥匙,每个轿厢手中都有一个非钥匙的物品,在进入之前,将手中物品与钥匙进行交换,如果交换后手中是钥匙,则可以输出抵达该楼层,如果不是,则等待,直到交换到钥匙。这样实现确实会占用CPU资源,但由于起初设计时,每个轿厢抵达tranfer层完成操作后便会立马离开,故等待时间不会太久,影响暂且忽略。

由于迭代过程中只进行了类的增添和部分方法内部代码的修改,仅展示最终的类图:
类图

bug分析

第五次作业

此次作业强测和互测均未出现bug。

第六次作业

强测没有出现bug,但互测中时出现:由于没有考虑到多台电梯reset的同一时刻涌入大量请求这一极端情况,导致当在给定时刻5台电梯同时reset时,该时刻的所用请求全部进入没有reset的那一台电梯。从而致使RTLE的产生。
解决方案:起初希望在五行内解决问题,于是设计reset后sleep,但sleep的时间难以把控,如果偏小,RTLE问题仍难以解决;如果偏大,则reset的电梯可能在此期间输出超过两次arrive。由于上述困难,最后选择在电梯reset时仍分配请求,在reset结束后再输出receive。

第七次作业

此次强测和互测出于同一个bug:在修改第六次作业bug时,没有考虑当电梯处于open状态时读入reset,从而致使reset后有request没有receive就in。
解决方案:添加缓存区,在reset时分配的请求添加进缓存区,在完成后再放入RequestTable里的队列。

互测hack到的bug

  • 在reset时对request分配的处理不当导致的RTLE
  • 线程安全问题,如对单例模式对象未进行保护措施

心得体会

总体而言,本次作业是一次不断有新的发现的历程,从初始完全仿照练习题对多线程安全的处理,到后来开始思索如何更精细的去使用synchronized,以及什么情况下使用线程安全容器、读写锁。也逐渐对wait-notify模式有了认知,线程start进入就绪队列,由系统程序控制进入执行队列,如遇wait则进入阻塞队列,等待notify(notifyAll)唤醒。同时,也使我对对通过强测并不代表没有问题这一观点有了具象的认知,以及在多环境中评测,构造极端样例更为关注。
略显遗憾的是,对同步块更精细地控制只停留于概念,在实际操作中频频出错而没有使用;同时由于希望实现较为简洁,策略仅限于基准,对调参的参数确定和影子电梯实现等难题没有进一步的探究,也没有考虑将双轿厢电梯拆开进行道路选择的优化。

  • 27
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值