2021 BUAA OO UNIT2 多线程电梯总结

2021 BUAA OO UNIT2 多线程电梯总结

 


 

前言:本单元作业是OO中比较经典的多线程电梯问题,主要考察的是有关于多线程和电梯调度算法的相关知识。在本单元中,笔者的代码共经过两次的迭代开发,从最初的单部单型号电梯发展为最终的多部多型号电梯。相较于第一单元的多项式求导问题,本单元的三次作业在代码架构上并不需要很大的改变。因此接下来当涉及到相似的部分时,笔者将着重分析第三次的代码内容。

 


 

锁和同步块的设计

Homework 1

 第一次作业的逻辑较为简单,我的输入线程和电梯线程之间只有一个共享对象,包括了调度器分配给电梯但还未进入电器的请求队列waitQueue和输入结束标志stop。由于请求队列我采用了具有同步特性的Vector类,所以只需要考虑对于notifyAll、wait、sleep等与多线程相关的方法加上sychonized关键字进行同步控制。

Homework 2

第二次作业和第一次有所类似,但是由于加入了多部电梯的要求,我对于我的结构进行了如下的调整:首先设置一个总的共享对象,包括从输入线程中读取的请求队列waitQueue和输入结束标志stop;其次再设置多个(与电梯数量保持一致)共享对象,包括从调度器分配给电梯但还未进入电器的请求队列waitQueue和输入结束标志stop。前者为输入线程和调度器线程产生交集的对象,而后者则为调度器线程和每个电梯线程产生交集的对象,调度器线程则负责把总请求队列中的请求合理地分配到每个电梯的请求队列中。同上,由于这些队列均采用了具有同步特性的Vector类,因此只需要考虑对于notifyAll、wait、sleep等与多线程相关的方法加上sychonized关键字进行同步控制。

值得注意的是,我在第二次作业中出现了一些花费很多时间才找出的多线程问题,比如我的电梯线程可能会一直保持沉睡的状态,最终我采用了总调度器完成分配时唤醒每个电梯线程的方法解决了这一问题。

Homework 3

第三次作业相对于前一次作业处理起来更加棘手,导致我虽然通过了所有测试但是对于锁逻辑的理解并没有十分清晰。由于加入了转乘这一需求,我在每个电梯线程中加入了一个转乘的idList来确认该电梯需要完成的转乘任务是否全部完成,由此来确认是否要将该线程终止。其中总调度器和电梯线程均会对idList进行操作,因为这样我需要给相关涉及读写的代码块和方法加上sychonized关键字进行同步控制。

我认为,对于初学者来说,锁的使用中要尽量避免嵌套关系,这样可能会出现死锁的bug,我在刚开始也使用了嵌套的锁,但是为了安全起见最终弃用了。且由于直到最后一次作业也仍然对于锁的使用和理解不够深入,所以到采用了繁琐但相对安全的物理锁,在进一步的学习中,可以考虑采用一些同学所说的逻辑锁来使得代码更简洁、逻辑更清晰。

 


 

 

调度器的设计

Homework 1

从严格意义来说,我在第一次作业中虽然已经抽象出了调度器这个对象,但是我对于其的理解只是实现电梯的策略而不是对于需求的调度。这样一个不太符合规范的设计也使我花费了很多时间来debug。我定义了一个共享对象,包括了调度器分配给电梯但还未进入电器的请求队列waitQueue和输入结束标志stop,针对于三种模式,我的电梯的运行策略通过书写状态机状态转移逻辑,完成了ALS算法。输入线程结束时将输入结束信号置True,电梯读取这个信号并在请求全部完成时结束线程。

Homework 2

由于第二次作业需要调度多个电梯,我对于架构进行了重构。由总调度器线程将输入得到请求分配给各电梯,每台电梯内置一个策略类完成单电梯运行的指引。其中输入线程和总调度线程共享一个对象,其中该对象含有一个标志着输入结束的标志stop。而总调度器和每个电梯线程之间共享一个从调度器分配给电梯但还未进入电器的请求队列waitQueue和输入结束标志stop,总调度器需要将请求从待调度队列拿出并放入目标电梯的等待队列,相应电梯从自己的等待队列中获取请求并执行。输入线程结束将结束信号传递给总调度器,再由总调度器传递给各个电梯,各个线程完成自己的工作并且调度器分配结束后,电梯线程就可以终止。

Homework 3

关于我的第三次作业,因为在第二次作业的基础上新加了转乘的需求,所以这次作业不仅需要完成一般请求的分配,还需要完成需要转乘的请求分割后的分配。

对于请求的分割,我采用了弗洛伊德算法,计算出了每两层楼之间的最优调度策略,并且根据该算法来分割需要转乘的请求。

这次的进程结束判断,不能通过输入的结束和调度器分配的结束,因为对于某个电梯线程还可能有转乘过来的需求。所以我在每个电梯线程中加入了一个转乘的idList来确认该电梯需要完成的转乘任务是否全部完成,由此来确认是否要将该线程终止。

 


 

功能设计与性能设计的平衡

Homework 1

UML图

Homework 2

UML图

Homework 3

UML图

 

协作图

 

扩展性分析

经过分析,发现其针对于电梯的扩展性较好,针对于调度算法的扩展性也较好,但是对于策略的扩展性不是很理想。

首先,我的电梯、调度器、输入等类之间的耦合度很低,可以认为每个类都只顾着处理自己的事情,没有过多的插手别的类的事情。因此我可以较为放心地对于调度器的调度策略、电梯的运行策略以及不同种类输入的处理等方面的代码进行一定的修改。

经过我的思考,我发现对于本次作业可以采用状态模式和策略模式来优化整体架构,并且可以在很大程度上增加代码的可扩展性,例如:将电梯的上升、停止、下降等作为状态模式来理解;此外,也可将morning、night、normal等作为策略模式来理解……这样可以在一定程度上提高代码的可读性和可扩展性。

此外,我还想到了引入工厂模式来使思路简洁明了,这样不同类型的电梯只需要传递不同的参数即可创建不同的电梯。

第三次作业中,由于多线程之间关系有所改变,我依旧出现了多线程相关问题:仍然是会出现某些电梯线程无法结束的问题。最终我采用了每个电梯线程结束后唤醒其他电梯线程的方法,但这样并不是一种很好的方法,增加了电梯之间的耦合度,不利于接下来的扩展,但我当前仍未想到很好的解决方法。

 


 

自己程序的bug

由于在写代码的时候我思考得十分仔细,并且也进行了充分的测试,三次作业的强测均未出现bug,第一次作业的互测被hack出了一个bug,其他均通过。下面主要来说一说我在自测中遇到的问题。

Homework 1

第一次作业在自测中出现了电梯线程无法结束的问题,经过我的分析,最终发现我的电梯没有添加最高层和最底层的约束,导致我的电梯可能会无限向上,最终导致超时。

此外,在这次的互测中,因为我设计的morning算法是等到9个人再来选择6人进入电梯,这样会存在不断轮询导致CPU超时的问题。

Homework 2

第二次作业总体来说,完成的较为顺利,但是也出现了一些问题。

第二次作业中,我依旧出现了多线程相关的错误:可能会出现某个电梯线程无法结束的问题。对于该问题,最终我采用了总调度器完成分配时唤醒每个电梯线程的方法解决了这一问题(代码如下)。

Homework 3

第三次作业总体来说也较为顺利,我共采用过两种分配策略,因为第一种出现了一些情况下的极度影响性能的问题和bug,所以我最终采用了弗洛伊德算法来完成电梯的转乘。

第三次作业中,由于多线程之间关系有所改变,我依旧出现了多线程相关问题:仍然是会出现某些电梯线程无法结束的问题。最终我采用了每个电梯线程结束后唤醒其他电梯线程的方法,但这样并不是一种很好的方法,增加了电梯之间的耦合度,不利于接下来的扩展。

 

 


 

发现别人程序bug所采用的策略

在第二单元中,由于代码阅读起来相较于第一单元更加困难,且多线程的测试相较于以往的常规测试也更加复杂,所以这单元无论是测试的量,还是测试的深度都不如第一单元。

在这三次作业中,我首先通过梳理状态转移图来确保我的电梯不会出现违背常理的行动;其次针对于我的电梯策略我写出了一个策略相同的C程序来进行深入的测试,以确保算法的正确性;再者,我检查了锁的设置,防止出现较为***钻的多线程相关问题;最后,数据测试主要通过构造极端数据的方式进行,针对不同的输入模式、加不同的电梯等情况构造测试用例:例如在请求的楼层跨度上我考虑了大跨度楼层分布,小范围楼层分布,相邻楼层分布等情形。

这些策略也正是我hack别人的策略,即从状态转移、电梯策略是否考虑全面、锁的设计、极端情形是否考虑等方面审查别人的代码,并构造针对性的测试数据。

在第一次作业互测中,我成功hack了一次,为“电梯吃人”的bug。可能是我一直在A组的原因,由于大家多设计出了十分优秀严谨的代码,大多时候我都是无功而返。

 


 

心得体会

 第二单元主要学习了多线程的问题,其中我收获最大的就是同步和线程安全相关的知识(当然,这个知识点也是我出错最多的地方)。

此外,经过这一单元的磨练,我对于层次化设计有了更深的体会:从输入到调度器再到电梯的层次架构在我的不断改进下终于得以很好地体现;对与状态模式和策略模式也有了初步的了解,为我将来写出可扩展性更好的代码奠定了基础。

虽然在设计架构的时候已经进行了充足的设计和充分的思考,但是“理想很丰满,现实很骨感”,在实际写代码的过程仍然与最初设计在实现上发生抵触,所以做出了一些并不是很好的调整和妥协。但是这对于初学者是不可避免的事情,只有不断地体验重构的痛苦并吸取宝贵的教训,才有可能在今后写出简洁、易读、安全、高效的多线程代码。

 

posted @ 2021-04-27 21:47  for_wheat  阅读(90)  评论(0编辑  收藏  举报