BUAA_OO_2020_第二单元

BUAA_OO_2020_第二单元

part 1.作业及评测分析

第五次作业

类图:

第五次作业采用了典型的生产者消费者模型,ELevator类和RequestReceiver类共享同一个托盘(RequestTary)。

1.设计思路

1.requestReceiver线程读入数据,并解析完成创建person的过程,然后将person加入到共享队列中;
2.elevator线程的思路:由于这次只有一部电梯,所以需要用该部电梯完成全部的载运任务,所以其实只要是发出请求的人都需要搭乘这部电梯,所以请求队列就是电梯的运载队列;电梯内部加入一个现在在电梯中的person的队列;当人搭乘电梯时,将其从电梯的运载队列中取出,加入到电梯队列中;电梯最最主要的任务便是确认下一次停靠的楼层,这取决于采用的算法,这三次作业中我均采取的look算法(一般的电梯在开门后一定会载客,而我的实现有些许不同$^1$),电梯确定楼层的思路是,首先要响应电梯内乘客的需求,寻找最近的楼层请求,在运行的时候,每运行一层,在该楼层寻找可携程的person,如果现在电梯没有目的楼层而且电梯为空,则先查找按照目前的运行方向可到达的楼层有无同方向的请求,如果有,则响应最近的同方向请求;如果没有,则查找最远的反方向请求,如果依旧为空,则改变电梯的运行方向,重复上述过程。
[1]以下情况与真实look不同:
比如电梯在4楼,现在5-15层只有一个人,ed 1-FROM-7-TO-3
在电梯到达7层开门后,我会再次查询,此时发现来了别人,ed 2-FROM-8-TO-9
这时我会放弃搭载1而去寻找2,这是为了减少折返的时间。现在发现有更好的解决方案,每次多开门都会影响性能,而且这次电梯没有容量限制,所以最好是把1捎带再去寻找2,这样可以减少一次开门的时间。
3.典型的生产者消费者模型,主要需要注意的是结束的条件,首先必须要求输入线程结束而且电梯运行结束才可以,我选择在共享托盘中加入一个标志位,标志输入线程是否结束,这样可以在线程中交互信息,我会在输入线程结束后,尝试唤醒电梯线程(为了应对空请求或者错误请求,试想输入线程还未结束,电梯运行结束正在wait,最后输入线程来了一条不能向共享托盘中加person的数据并且以之结束,电梯线程便会一直等待)。

2.复杂度

本次作业主要是Elevator的run方法严重超标,部分代码没有封装成方法,导致复杂度很高。

第六次作业

类图:

这次作业其实仔细分析发现可迭代性非常高,本次作业我对电梯类做的改变仅仅有max这个限制,其余的全部改动均出在RequestReceiver这个类,这次要完成对人员的分配。

1.设计思路

我对每个电梯都建立自己的托盘,而requestReceiver线程可以托管全部的托盘,完成人员的分配,而这次的分配也比较简单,我最开始采取了楼层分配,每个电梯分管各自的楼层,但是这样设计遇到同楼层请求很多的任务就会表现出极差的性能,所以我采取了轮转分配。

2.复杂度


这次作业由于电梯的主体没有改动而且加入了max的限制,导致复杂度单调递增,主要还是体现在run方法以及getIn方法中(与max相关)

第七次作业

类图:

第七次作业让我真正体验到多线程的快乐,目前还有没有解决的小问题,这次代码共有三版,第一版由于我盲目预测需求(其实就是按照往年指导书做的,完全没料到动态电梯这一高招);第二版是我的痛点,目前这版代码弱测没有一次完整通过,但是在bug修复中居然通过了全部强测测试点,而且本地现在依旧没有复现(代码的确有不足,但是按照我的分析只会发生ctle,然而报错却是re,非常好奇);第三版就是最终版。先来分析设计思路。

1.设计思路

有四个问题值得讨论,一个是人员的换乘和搭乘策略,第二是同类电梯的分配,第三个是电梯的动态加入,第四个也是最重要的,电梯线程的结束。
1.人员的换乘和搭乘策略:我选择了固定式的,没有和电梯进行交互,我觉得那样耦合度会非常高,不太符合代码的设计要求,于是采取了固定式的达成策略(也导致强测比较一般),其实就是分析人员的出发楼层和结束楼层,确定这些楼层都在哪些电梯的可达域中,如果可以直达就直达,否则需要确定换成楼层,我的原则是ta搭乘的第一般电梯应该能让他尽可能接近终点(第二般等太久可以爬楼不是)。当人员从一个电梯下来,判断ta是不是还要接着坐,坐那种,然后加入其它队列。
2.同类电梯的分配,我对A,B,C三个类型给了一个ArrayList,在这个ArrayList中轮转分配。
3.电梯的动态加入,我选择了取巧,为了方便管理(减少代码量),我最开始就建立了六部电梯,电梯中加入了refresh方法,可以更新电梯信息
4.与前两次作业不同,换乘导致电梯的结束需要更加细致的判断,试想A中有人想换乘B类电梯,结果B类电梯一个每剩下,岂不很惨。我的第一个设想就是,参考了java的finalize方法,加入了中间态,给了电梯线程一个苟延残喘的机会,当所有适合单部电梯停下的条件都符合的时候,进入中间态,然后告诉其他电梯我进中间态了,其他电梯再判断;当所有电梯都进中间态,电梯一个一个停下。这中间涉及到连续的wait,notify的过程,我用visuaVM分析也的确发现了CPU时间比较高的情况,但是re到现在还是有疑惑。

现在的版本则是这样,当person构建完成,对于ta所需要搭乘的每一类电梯都计数+1,当电梯的计数为0且电梯停止运行而且输入线程终止就结束一个类别的电梯CPU时间瞬降。

2.复杂度


依旧是run和getIn一统江山。

评测

1.强测

这次强测中均为发现错误,虽然look算法和轮转分配达不到非常好的效果,但是三次作业均分也在98+。

2.互测

提到互测不得不提评测机,没有评测机的互测是没有灵魂的,想提一下评测机器的实现:
1.首先是定时投入,其实非常简单,只需要提取时间,然后照抄睡排法。
2.自己写check程序,这个就是按照电梯的运行模拟。
3.善用管道(shell的管道非常好用,主要是简单,但是没法实现rtle做的检测),所以学习了python的subprocess。
4.create,上个单元正则学了那么久,相信这个完全不是问题。

但是评测机好鸡肋啊,三次作业都没有凭借评测机找到别人的bug,然而第三次手动测试中发现真的有人忽略了最后来的无效指令(其实不是,只是在最后加电梯),隔别4周,再次刀人的感觉,就一个字。

part2.收获

多线程

这三次作业真的让我体会到了多线程是什么,而且通过OS和查阅资料也逐渐了解了底层机制,很多难以实现的功能,也借着多线程学习的深入逐一解决。

迭代

迭代太爽了,迭代爽了,重构是不可能香的,第一次作业完成后,后两次作业(第三次和re的对抗不记录的话)基本在几小时之内就可以解决。

猜你喜欢

转载自www.cnblogs.com/lphoebe/p/12701182.html