301
社区成员
发帖
与我相关
我的任务
分享目录
本单元的迭代作业主要是完成一个多线程实时电梯系统,主要考察的是多线程相关的知识
第一次:完成一个由六部电梯的电梯系统,乘客的请求指定特定的电梯
第二次:在第一次作业的基础上新增电梯的重置操作,重置可以重新设置相应电梯的运行速度和满载人数,同时乘客的请求不再指定电梯,还新增了receive的约束
第三次:在第二次作业的基础上又新增了一种新的重置操作,可将电梯重置为双轿厢电梯,同时设置双轿厢电梯的换乘楼层、满载人员以及运行速度
第一次作业共有如下几个类:

Main类和InputThread类主要是负责整体的输入和输出,Elevator类主要是处理单部电梯接送乘客的行为以及对乘客请求的调度策略,TimeCmp、ElevatorSystem主要是用于处理整个电梯系统的调度,因为本次作业的乘客请求都是指定了对应的电梯,所以这一部分实现较为简单,但在后续的迭代作业中这一部分难度较大;Floor类、Person类以及PersonQueue类都是处理乘客请求相关的类,TestMain用于测试代码
第一次作业代码量:

类图:

本次作业采用的是生产者-消费者模式,刚开始将读取到的所有的乘客请求都存储在一个等待队列中,电梯在处理乘客请求时只需从这个等待队列中将乘客请求取出,再根据自己的运行策略运送乘客即可。
类复杂度:

第二次作业在第一次作业的基础上新增了对电梯的重置以及乘客请求不再指定电梯,需要我们自己设置合适的调度策略来选择相应电梯

第二次作业在第一次作业的基础上新增了一个Counter类,主要用于电梯线程在运行时辅助进行相关行为状态的操作,然后主要新增的部分在于Elevator类中新增了电梯reset行为的相关行为,以及在ElevatorSystem中新增电梯的调度策略。本次作业中我采用的调度策略是计算权重,通过考虑电梯与请求相距楼层,电梯运行时间等因素计算出每部电梯对于该请求的权重,再选择最佳权重的电梯作为处理该请求的电梯即可
第二次作业代码量相较于第一次作业代码量有显著提升,主要是在于Elevator类中新增的对电梯reset的相关操作以及ElevatorSystem中新增的电梯调度策略

第二次作业类图:

类复杂度:

第三次作业在第二次作业的基础上新增了将原电梯重置为双轿厢电梯的操作,在进行迭代作业时,除了要实现对电梯进行重置外,还要实现新的对重置后的双轿厢电梯的调度策略,个人认为本次作业的思维难度是这三次迭代作业中最大的

与第二次相比,第三次作业类的数量没有变化,主要新增的在于Elevator类中新增的将电梯重置为双轿厢电梯的实现,以及ElevatorSystem类中新增的考虑了双轿厢电梯的调度策略
第三次作业代码量与第二次作业相比也有较大提升
类图:

第三次作业的电梯系统的调度思路与第二次作业相似,都是通过计算权重并选取最佳权重来决定由哪一部电梯来处理乘客请求,三次迭代作业中都采用的是第一次作业中所用的生产者-消费者模式
类复杂度:

我认为在这三次迭代作业中稳定的内容首先整体的设计模式框架是比较稳定的,均采用的是生产者-消费者模式,然后单部电梯的运行策略是稳定的,在第一次作业确定电梯的运行策略后,在后续的两侧迭代作业中变化不大;然后易变的内容就是整个电梯系统的调度策略和电梯的重置操作,电梯的调度在第一次作业中是指定的电梯,第二次作业中需要我们自行设计调度策略,第三次作业中又新增了双轿厢电梯的调度,电梯的重置操作除了第二次作业中重置相关参数以外,还有第三次作业中将电梯重置为双轿厢电梯的操作,我认为这两部分是易变的
我三次作业中所用的整体框架的线程协作关系大致如下

在这三次迭代作业中,我均使用的是synchronized锁来维护线程安全,对于涉及电梯reset,电梯系统调度以及乘客请求队列相关的操作,我均采用synchronized关键字对相关方法和对象进行修饰以保障线程安全,因为在作业中有大量的读写操作,故第七次作业实验题中提到的读写锁也是一个很好的选择,但由于之前的迭代作业中我的框架全都采用的是用synchronized关键字进行修饰的方式,在完成第七次作业时我还是选择沿用之前的思路,也就没有使用读写锁
第一次作业只涉及一个由六部电梯的电梯系统,乘客的请求指定特定的电梯,整体思路上没有太大难度,我的代码也只有一个小bug,就是我的关门的方法只涉及电梯状态的改变,在使用时要注意调用方法后加上sleep()语句以模拟关门的时间,但我在电梯运行完所有请求后最后关门时忘记加sleep语句导致出现关门过快的bug
第二次作业有一个bug是乘客请求未完成,主要就是因为我的电梯整个处理过程结束太快了,最后有reset时会弹出当前的receive,但此时电梯调度已经结束,就没对它再进行调度;还有一个bug是乘客被重复receive的问题,原因可能在于我在reset结束后同时输出receive,就有可能出现receive在resetend之前出现的情况,就饿可能被判断为乘客被重复receive,然后我就在resetend后手动延迟一下,保证receive一定在reset结束过后才输出
第三次作业的思维难度是本单元三次迭代作业中最多的,同时bug也是最多的,首先就是刚开始时有一处锁逻辑上本应该是电梯在移动时才会锁住,但在电梯没有移动时还是wait了,使得程序没有唤醒这个锁导致程序卡住;还有一个电梯不存在的bug,原因就是电梯重置为双轿厢电梯后仍然加入了未被重置的单轿厢电梯的调度,使得在处理请求时出现将双轿厢电梯当作单轿厢电梯使用的情况;还有一个bug是电梯门连续开两次,原因在于乘客刚好在双轿厢电梯的换乘楼层上电梯,但上的电梯无法到达请求的目的地,需要换乘,导致电梯会连续开门
我在debug时主要还是采用在程序运行的各个阶段添加额外的输出,输出我想要得到的相关数据,在本地运行样例代码,观察额外输出的数据,通过观察这些额外的输出,就能比较容易的找到程序是在哪一步出现了什么问题,这种方法主要用于二三次作业修复bug当中,第一次作业主要就是通过课程网站测评反馈的信息修改bug
我在第三次作业中避免双轿厢电梯相互碰撞主要是通过一个series()函数来实现的

通过判断电梯方向向上且为A轿厢或是电梯方向向下且为B轿厢时必须换乘以实现避免双轿厢电梯碰撞
本单元三次迭代作业主要考查的是多线程方面的知识,在学习本单元之前,我对多线程完全不了解,因此在开始完成作业之前还需要对多线程的知识进行自学,这方面的知识理解起来也有一定难度,这也加大了本单元的学习难度;本单元的三次迭代作业,虽然代码量上比起第一单元的作业小了一些,但其整体的思维难度却比第一单元更大,主要在于电梯的调度策略以及进程安全等方面,如果对多线程的知识掌握的不够熟练的话就很容易产生死锁等bug。总而言之,这个单元的学习虽然非常困难,但也很大程度的锻炼的我编写程序以及解决bug的能力,经过这单元的学习,虽然存在对知识掌握的不够熟练的情况,但我还是对多线程的知识有了一个基本的掌握,也对层次化设计以及面向对象的编程有了一个更深的了解。