OO第二次单元总结——电梯多线程调度问题

OO第二次单元总结——电梯多线程调度问题

在这个单元OO学习中,我们终于迎来了期待已久(不是)的电梯多线程调度作业,开启了OO打怪之路的新关卡。虽然说经过了这三次作业,我对于多线程的理解还不能算是熟练,在线程控制方面的实现也仍有缺陷,但是经过了磕磕绊绊的写作业过程也达到了可以对多线程进行简单应用的程度。在此,我对于第二单元的OO作业进行一个总结,梳理一下自己这三次作业中的实现方式以及所踩过的大雷。


第一次电梯作业

(1)设计策略

  • 题目要求

    单部多线程傻瓜调度(FAFS)电梯的模拟,实现对于一个目的选择电梯的傻瓜调度。

  • 设计思路

    在第一次的电梯作业中,我基本是严格按照指导书中建议的构架模式进行电梯调度。

    考虑到对于输入和电梯,等待人员队列是共享的,所以我将队列作为共享变量在输入和电梯之间传递。除此之外,我将输入操作放入了主线程中,并将得到的等待人员信息传入队列中进行添加等操作。对于电梯,则单独开了一个线程,该线程可以读取队列类的信息,并依次对队列中的等待人员进行输送的操作(本次作业没有考虑捎带和优化,使用的是最为傻瓜的调度方式——按次序读取等待人员并操作——先来先送)。

(2)基于度量分析程序结构

  • 总体结构介绍

    在本次作业中,我总共分了4个类——一个主线程类、一个电梯线程类,还有另外两个辅助类——人员信息变换格式类、电梯运行类。其中,在主线程中进行输入操作,并且主线程中包含共享的队列,输入后的信息存储如队列以供电梯进行获取,电梯不断进行轮询直至队列内为空且输入已关闭。

  • 类图

从类图中可以明显地看出整个程序的结构,由于第一次电梯作业还算比较简单,所以构架也很清晰,但是由于没有进行调度器的考虑,导致我到第三次电梯作业时的程序架构上出现比较大的问题。

  • 主要度量图

由于本次作业的实现比较简单,需要的交互也不是很多,代码量也没有很大,所以在本次作业的代码构架方面没有出现什么问题。

  • UML的协作图(sequence diagram)

  • SOLID原则分析

    S(单一功能原则):本次作业主要有三个部分——输入、调度和运行,基本实现了不同的类有着不同的功能。

    O(开闭原则):由于在本次作业中没有使用接口和继承,所以该原则没有办法遵守。

    L(里氏替换原则):本次作业中没有涉及多态。

    I(接口隔离原则):本次作业没有使用接口。

    D(依赖反转原则):在本次作业中由于需要实现的功能仍不是很多,所以高层次对低层次的依赖还不是很强。

(3)bug分析

由于这次的作业比较易于实现,并且代码结构没有那么复杂,所以我没有发现自己和他人的bug。


第二次电梯作业

(1)设计策略

  • 题目要求

    单部多线程可捎带调度(ALS)电梯的模拟。该电梯可由-3层到-1层,1层到16层。

  • 设计思路

    在第二次电梯作业中,我也主要是按照指导书上的方法写的。将需进入电梯的请求分为两类——主请求和被捎带请求,将电梯内的人员作为一个list。

    在这次作业中,我为等候人员队列开了一个单独的类。当电梯中list的size为0且等待队列不为空时,首先考虑使等待队列中的等候最久的人进入电梯。同时,在经过每层时,都对是否有人员需要在此层上下进行分析,做到捎带。

  • 优化

    在做本次作业时,为了做到正确性(虽然最后也没做到),没有进行过多的优化。现在想想,在电梯为空时,应当选择离当前楼层最近的一个等待人员进行进入电梯操作,这样能够减少空电梯自己跑这种浪费时间的现象。

(2)基于度量分析程序结构

  • 类图

本次作业我基本延续了上次的结构。主要的不同之处是将等待队列提取出来,通过调用FloorList类进行操作。但是问题还是在于我仍然是直接在Elevator类中进行调度,应该单独开设一个调度器。

  • 主要度量图

可以明显地看出由于直接在Elevator类中进行调度,导致Elevator类的复杂度相较于其他的类过高。这也提醒我在以后的代码编写时要更好地简化类,区分不同的功能。

  • UML的协作图(sequence diagram)

  • SOLID原则分析

    S(单一功能原则):同上次作业相同,本次作业主要有三个部分——输入、调度和运行,基本实现了不同的类有着不同的功能。(但是其实调度部分可以更加细化,目前实现调度功能的类过于复杂。)

    O(开闭原则):本次作业中没有使用接口和继承。

    L(里氏替换原则):本次作业中没有涉及多态。

    I(接口隔离原则):本次作业没有使用接口。

    D(依赖反转原则):在本次作业中还是仍然依赖于具体的类。

(3)bug分析

  • 自己的bug

    本次作业在强测上栽了比较大的跟头,但是仔细看了一下自己的代码,发现其实只是一处控制关门的信号应该为0的时候写成了1。然而这一个只有一字之差的错误却使我的强测只对了3个点(欲哭无泪)。这血泪的教训也提醒我在之后写代码时一定要思路清晰,并且要全面进行debug,并仔细查看一遍自己的代码。

  • 他人的bug

    在本次互测中,由于不知道该如何造出易错数据而没有能够找到他人的bug。


第三次电梯作业

(1)设计策略

  • 题目要求

    多部多线程智能(SS)调度电梯的模拟。总共有三部电梯,分别只能停靠规定的楼层,每个电梯的最大载客量也不尽相同。

  • 设计思路

    在基本构架上,我基本延续了第二次电梯作业的思路(除了对于上空电梯的等待人员的选择上进行了一些优化),但是将轮询改为了wait、notify方式。主要分为了四种类:主线程中含有输入操作,等待人员队列为一个单独的类,电梯的调度过程为一个类,人员的类型为一个类。

    在进行 输入时,我首先进行的是对于人员进行分类——主要可以分为9种状态:只用A电梯即可送达、只用B电梯即可送达、只用C电梯即可送达、A->B(不能只用A电梯的情况下)、A->C(不能只用A电梯的情况下)、B->A(不能只用B电梯的情况下)、B->C(不能只用B电梯的情况下)、C->A(不能只用C电梯的情况下)、C->B(不能只用C电梯的情况下)。

    在本次作业中最主要的问题在于当等待人员上楼和下楼的楼层无法用同一个电梯满足时,应该如何使其更换电梯。在此处我进行了一些优化,将人员可以进行更换电梯的楼层根据他们本身处于的楼层设置一个区间,若本身就有人员在此处上下,则可以顺便将这个人放下,节约一些开关门的时间。

  • 优化

    在最初的代码完成之后,我一直在思考怎么进行进一步的优化。在第一个代码版本内,每一个人员的状态是不能够重合的,这就面临着一个很消耗时间的问题,若电梯已经满了,且还有人员没有上去,那么本身这些人员是可以运用另一种方法被运送的,但是在这种情况下这能等待。但是当我完成代码的改写后,发现除了在这种比较极端的情况下速度可以将近快出一半以外,在其他的情况下并不能很大幅度地提升速度,甚至还会出现很多bug(栽了强测)。

    除此之外,考虑到每个电梯的速度不同,所以在编写代码时,还可以对于电梯的选择进行控制,如在既可以上A电梯也可以上B电梯的时候需要保证上A电梯。

    在我的代码中,还有一个比较花费时间的问题是我都是采用先上后下原则,所以很多情况下能够一起带走的人员却被留到了那里,所以其实还可以将电梯调节成为先下后上,这样便可以节省很多电梯多次来回跑的时间。

(2)基于度量分析程序结构

  • 类图

由类图中可以看出,本次的程序结构与第二次电梯作业差不多。这个构架在前两次电梯作业中都没有什么问题,但是可以发现,在本次作业中,由于程序更加复杂,所以每个类的函数及变量变得更加复杂。

  • 主要度量图

从度量图中可以看出,在本次电梯作业的代码中,由于函数中判断语句过多,类的功能划分不明确,许多类和函数都在复杂度上出了问题。明显可以看出用于调度的代码的行数过多,应该将其的功能更加细化,并按照功能多划分几个类。

  • UML的协作图(sequence diagram)

  • SOLID原则分析

    S(单一功能原则):在本次作业中,可以明显看出在电梯调度方面没有进行进一步的功能细化,导致一个类承担了多个功能的实现。

    O(开闭原则):本次作业中没有使用接口和继承。

    L(里氏替换原则):本次作业中没有涉及多态。

    I(接口隔离原则):本次作业没有使用接口。

    D(依赖反转原则):在本次作业中主要依赖于具体的类,没能符合依赖反转原则。

(3)bug分析

  • 自己的bug

    由于在进一步优化的时候没有进行更加全面的思考,完全是补丁式的优化,导致在很多时候程序的运行无法按照控制进行。

    当然,问题出现的主要原因还是在自己debug的时候没能进行全面的测试,比如说,我在自己测试的时候测了B->C的情况,却没有测C->B的情况,而程序也就是在这里翻了车(后悔莫及)。

    • 经验教训

      以后在自己进行测试的时候,数据一定要构造的更加全面,一定不要想当然。应当根据题目的意思构造覆盖各种情况的数据。

  • 他人的bug

    这次作业bug的随机性还是比较大的,主要我认为会出现在三个错误上:

    1. 停不下来

      这是主要需要构造一些较为复杂的数据集,其中需要包含各种情况,这样便比较容易命中bug。

    2. 人没进去

      这个人工de还是很困难的,主要方法有观察代码并进行分析、碰运气

    3. 线程出现不应该的error信息

      这种情况主要是由于在人员出电梯是没有将循环变量减1,导致随后出现error现象。


心得体会

  • 线程安全

    在目前编写多线程的经历中,我认为线程安全最重要的还是解决如何对于共享变量加锁的问题。

    在这三次作业中,我主要使用synchronized对于共享的方法、对象进行上锁,并且使用notifyAll将锁进行释放。

    除此之外,还可以使用原子类保证我们对这些类进行操作时是线程安全的。

  • 设计原则

    这三次电梯作业没有能够很好地符合SOLID原则,在代码编写时没有过多地考虑进行类的抽象,也没能使每个类的功能更加具体,导致代码过分冗长。

    对于接下来的oo作业,需要在每次代码的编写前都更加清晰地知道自己需要实现哪些功能,并再将功能进行细化。

通过这三次多线程电梯的作业之后,我目前对于多线程总算是入了个门,但是前路漫漫,在今后的oo之旅中,我也需要加强自己对多线程的理解,使自己的代码能够更加坚不可摧,而不是在发现bug后对着满目疮痍的代码打补丁。

原文地址:https://www.cnblogs.com/liuxy-7/p/10758546.html