OO第二次作业总结

OO~第二次作业总结

 连续三周的电梯作业结束了,总的来说这三次作业做的还算平稳,既没有被刀,也没有刀中别人。那么接下来开始谈谈我对这三次作业的认识。

一、设计策略

 我三次作业的设计思路基本上是相同的,电梯主要分成了三个线程,即Input(输入线程)、Scheduler(调度器)、Elevator(电梯)。在这三个线程之间通过两个不同的托盘进行数据交互,即把输入线程输入的电梯指令放入调度器的ArrayList中,然后再用傻瓜调度算法/可稍带算法将相应指令传给电梯,最后电梯执行指令并输出相应结果。

 第一次作业:由于第一次接触多线程,在程序设计层面上没有考虑到CPU运算时间,因此没有使用wait()notify()方法,托盘的设计也是单例模式,所以总体上来说整个程序更像是一个单线程(听说确实有人用单线程就过了测试)。

 第二次作业:增加可稍带算法,即电梯能从托盘中取出所有当前楼层指定方向的请求,在这个过程中,我通过两个队列upqueuedownqueue分别存入上楼请求和下楼请求。在电梯类中设置一个方向参数用来判断电梯(停靠为0,上楼为1,下楼为2)。由于测试中严格限定了时间,所以需要在调度器线程和电梯线程中加wait()用来等待下一条指令的到来,从而减少CPU的运算时间。

 第三次作业:我认为这次的作业侧重点在于三部不同的电梯,而这三部电梯停靠楼层、运行速度、电梯容量各不相同,这样在电梯初始化时需要增加特定参数进行设置。我在这次作业中设置了三个调度器与电梯之间的托盘,即经过电梯的筛选将不同指令投入不同的托盘中,电梯再从托盘中拿到相应指令即可。除此之外,本次作业最难的地方在于乘客的换乘,我的思路是将一个请求拆分成两个请求(前请求1,后请求2),即只有执行完请求1后才能执行请求2。在请求类中,我添加了一个nextrequest对象,当该请求没有后请求时设置为null,反之则为后请求。这样的话,在请求1执行完后,可以通过nextrequest得到请求2,改变该请求的transfer参数使它能够被电梯收入。

----------------------------------------------------------------------------------------------------------------------------------

二、基于度量分析程序架构

1.第一次作业

 这次作业现在回想起来有很多地方是没有用的,由于InshTray托盘的设计为单例模式,所以实际上调度器和电梯中的指令始终为1,因此也就是实现最标准的傻瓜调度。

 在代码耦合度方面,由于采用了ArrayList存取算法,所以在调度器线程中耦合度会稍微大一些。

2.第二次作业

  第二次作业与第一次作业的整体结构是相同的,但是我在算法中实现了一些小优化,因此耦合度相比第一次作业是有所减小的。

3.第三次作业

 这次作业的复杂度相比前两次上升幅度明显,当然这也是可以预想到的。由于我第二次作业优化分不高,所以这次稍微写了一点优化算法,if语句嵌套较多,甚至出现了方法行数超过限定数量而不得不将其拆开的问题。总体来说,以牺牲一点代码结构的代价有效提升了性能。

 

----------------------------------------------------------------------------------------------------------------------------------

三、互测&公测

 在互测环节很遗憾没有找到和被找到bug,可能也是因为大家程序写的越来越完备了吧。

 值得一提的是,在第二次作业中,我的优化分数比较低,看了一些强测数据后,发现是在我电梯到达15层(最高层)后,下楼的阶段没有捎带客人。原因是我这次作业的设计思路是在电梯人走光时会根据upqueue和downqueue的人数选择电梯上楼还是下楼,在这个过程中,我写了一个gettofloor方法,即直接到达第一个上\下楼客人的楼层,这就导致在gettofloor的过程中没有捎带相关的客人。因此在第三次作业中,我还是老老实实的一层一层上下楼接送客人。

 总的来说,只要老老实实一步一步的搭建电梯,就基本上不会有很大的问题。这时我们就得让自己写代码时保持一个清醒的头脑,这才是最为关键的。

----------------------------------------------------------------------------------------------------------------------------------

四、关于如何debug

 由于我开始并不会实现[0.0]的输入,而在弱测阶段却出现了因为[0.0]时输入数据产生的bug。我的解决办法是在线程启动之前,全都sleep(4000),这样就有足够时间输入数据来debug。

关于bug的类型:

 1.输出错误,这个我在第三次作业出现过,即出现了漏指令输出的情况,这个错误相对来说是比较好找的,通过应该输出的内容来定位bug出现的位置。

 2.CPU超时错误,这个问题主要就是在Thread.run时一直执行while(1)中的内容而没有休息,cpu反复刷新导致计算量增大,这样就可以通过wait()和notify()来减少无用刷新的次数,并且我在调度器线程中每循环一次会sleep一小段时间。在解决了这个问题后,公测的cpu时间一般不会超过1s,如果时间过大,即使在允许cpu运行时间内,感觉程序也是存在隐患的。

 3.运行时间超时,这个问题在我最开始遇到的时候令我有点束手无策,原因是在我本地运行测试数据是正确的,后来发现该类问题出现主要是因为没有正常退出程序,即ctrl+D后,线程没有全部退出。我出现这个bug是因为我input退出时会传入一个flag参数告诉调度器退出,接着调度器传入flag参数告诉elevator退出,我在调度器和电梯每次轮询时都会进行一次判断是否flag被置为true。然而,我没有正确退出的原因是前面电梯停下的时候采用了wait()方法,这就导致flag值改变了但是并没有唤醒相关的线程,从而导致运行时间超时。

----------------------------------------------------------------------------------------------------------------------------------

 五、心得体会

 在实现电梯操作系统的过程中,我还是遇到了不少问题的。我认为多线程与单线程不同之处在于线程安全和交互上,很多时候写完程序后发现某个数据在这个时间点运行是对的,但是换个时间戳输入/输入后立刻ctrl+D就会出现问题,在debug环节确实感觉多线程会出现许多稀奇古怪的线程控制问题,我们最好的办法做一个加锁小王子了。

 对于电梯的优化调度而言,我认为不同的调度方法对于不同的请求队列结果是不一样的。在第三次作业中,我曾经思考过把这个调度问题变成一个数学问题,可以通过建模的办法解得当前请求最优分配的电梯,当然这个想法因为当时某些不可抗因素导致没有时间去实现,况且,我们无法预测未来可能出现的请求,所以添加的优化算法甚至可能会提高调度时间。

 综上所述,我们需要在保证安全性的前提下去提升性能,而不应该去为了某些特殊数据点设置特殊调度策略,这样的分我认为拿起来是无意义的,毕竟现实中并不可能有那样的电梯。

 经过这个单元的学习,我强化了面向对象的思维,并且学习了一些多线程协同模型,这对于今后更多复杂的多线程程序实现有很大帮助。希望在后续的学习中,少写一点bug,花更多的时间在优化层面上,这才是这门课程最有趣的地方~

posted on 2019-04-24 17:02  lufei1  阅读(136)  评论(0编辑  收藏  举报

导航