2020_OO_ Unit 2 Summary

table of Contents

  •  Three operations framework, implementation details and measurement
  • BUG self-test and mutual test
  • Experience and reflection

Job analysis

  First homework

    This operation is the design of a single piggyback elevator. It is mainly a preliminary understanding of multi-threaded design implementation and testing. The algorithm design itself is very simple. The main body of the program is composed of request receiving thread, public object request queue, and elevator thread . The only direct interaction between the elevator thread and the request receiving thread is to see if the request is over (the specific processing of the interaction here is shown in the experience part, which can actually be avoided), so it is very safe. Because there is no need to consider overloading and other factors, every time the elevator goes to the first floor, find out whether there is an elevator queue inside the elevator, and then find out if there are people in the public queue outside the elevator on this floor. direction. When there is no request to enter the elevator in the current direction and the elevator is empty, it turns; when the goal of everyone in the internal queue of the elevator is opposite to the current driving direction and there is no request to enter the elevator in the current direction, it also turns; when the top floor is reached Or turn at the bottom; don't turn at the rest. The final average score obtained by this algorithm is 97.9 points, the performance is acceptable.

  Second job

    This operation changed one elevator into 1 to 5 elevators with input decision, and added the setting of elevator load limit. The main structure of the program is still the same as the first time, but the public queue is changed from a unified queue to two queues divided upstairs and downstairs . The queue is the same for any elevator, and it is completely free to compete. The elevator only enters people who are in the same direction as the current direction of operation, which greatly improves transportation efficiency under limited loads. Algorithmically, when the elevator is less than or equal to 2, the LOOK algorithm is used, but in the specific implementation, it is found that it is easy to conflict with the point of "only enter the person who is in the same direction as the current operation", which leads to the continuous reciprocating BUG . The author's solution here is to add a buffer layer to the look algorithm , that is, to check whether there is a request to enter the elevator on the current floor and the floor in the running direction . When the total number of elevators is greater than 2, the scan algorithm is used directly, and each elevator is scanned between the top and bottom floors, while adjusting the initial running direction and the maximum height of the first rise to ensure that the elevators can stagger. The final average score is 98.9 points.

 

  Third homework

    In this operation, there will be no general elevator, but three different types of elevators, which can only stop at specific floors, which leads to some requests must be resolved by transfer. At the same time, instructions for dynamically generating elevators are added, but this part is relatively simple to implement. The main structure of the program is still the same as before, but the task of the scheduler maintaining the queue has increased dramatically. Regarding transfers, the author's main consideration is to achieve conciseness and balance under absolutely random data . Under such considerations, the author has selected a fixed scheduling strategy of at most one transfer , that is, each request has only one implementation method. When the request is determined, whether it needs to be transferred and how to transfer is determined. . The specific transfer implementation is that the request processing thread does not directly call the scheduler's put () method after receiving the request. The scheduler itself recognizes the request. If it is a request that requires transfer, the first part of the request is placed Into the queue maintained by itself, at the same time, the transfer count is increased by one, as one of the judgment conditions for the end of the elevator thread or sleep. There are six queues here, that is, there are separate queues for the three types of elevators up and down the stairs. When all kinds of elevators go to a specific floor, they will check the transfer: move the person who needs to transfer on the current floor out of the elevator, and call the dispatcher's put () to enter the request queue (ring lining strategy to ensure that it will not occur) New transfers, that is, people who do not arrive at their destination after coming out of the elevator, will be able to reach it again if they take another trip), and the transfer count is decremented by one. The elevator operation strategy is LOOK, and the final average score is 98.8 points.

  About the analysis of design principles such as SOLID (see the reflection section for scalability):

    SRP: It's awful. Each module is a class. The elevator and the dispatcher have dozens of methods and hundreds of lines of code, without further subdividing its various functions.

    OCP: It should be impossible to think about it. The scheduling algorithm penetrates into each class of elevator dispatcher, only the request processing class can do it.

    LSP: Yes, because there is no inheritance relationship designed by myself ...

    LOD: There is basically no direct communication between the elevator class and the request processing class, so there is no direct call.

    ISP: Only the RUNNABLE interface is implemented, which is obviously necessary.

    DIP: The level of abstraction is not well designed.

 

BUG self-test and mutual test

  BUG of your own program

    There were no bugs found in the strong test and mutual test of the three operations, which can be said to be very lucky.

  hack others

    秉持着人不犯我我不犯人的原则,仅对程序基本的功能作覆盖性测试,以及利用一些边界数据做性能上的检查,并没有自动生成大量数据捕捉特殊的线程安全问题(主要是技术手段有限)。性能上的检查也并没有针对某种特定的算法捏造数据,一是因为时间限制确实给的宽,二是将心比心,用这种这种不太算BUG的BUG搞同学心态似乎也不太好,因为确实有些同学的调度算法在绝大多数情况下性能很好,但是个别极端情况下会在正确运行时产生TLE,这只能说是一种权衡,不能算作错误。结果来看,第一次全房间无伤;第二次房间唯二两刀中,笔者贡献了一刀;第三次房间战况激烈,但是只有一位同学是在功能上出错,成为了集火的对象,其他同学只在个别时候出现死锁等安全问题,考虑到房间足够活跃,笔者选择了远离战火,以0/0,0/40结束了互测环节。

 

 

心得与反思

  本单元电梯作业的主题是认识多线程,尝试应用一些关键字、锁的机制保障线程的安全。三次作业的架构中,唯一的线程间直接交互,是电梯线程访问请求处理线程,判断请求输入是否结束。在这里有一个有趣的陷阱:每当电梯运行到一层,完成上下人以后,会访问请求处理线程,若发现输入还没有结束,但是现在公共等待队列是空,且电梯内没有人,则会wait。而请求处理线程发现输入结束后,会notify所有电梯。初看好像还行,但是如果在电梯访问请求处理线程得到输入没有结束时,输入正好结束,则有可能请求处理线程先notifyAll,然后电梯线程再休眠,从而导致程序不能正常结束。解决方法是在电梯线程中把访问请求处理线程的过程和wait一起锁在调度器上,同时在请求处理线程中把修改输入结束标志的过程也锁在调度器上。

  在线程安全方面,为了避免一个线程直接访问另一个线程,笔者的调度系统不得不放弃观察电梯的状态,只能采取同类电梯自由竞争的策略,虽然最终取得了不错的性能分,但是有逃避问题之嫌。现在想来,可以通过新建status board公共对象,完成对各个电梯状态和请求输入状态的记录,给其他线程访问。

  在实现拆分策略时,涉及到了多维度的分支嵌套,复杂度很高。第一次写该模块式时,为了节省行数,没有用层次化的分支展开,而是完全并列的分支,这对代码的检查和拓展制造了麻烦。后来以请求的进入楼层为分类依据,每个类单独建立拆分方法,根据目标楼层进行拆分,并完成将其加入至相应队列的任务。此外,idea会对分支判断中的冗余条件进行检测,并且提示删除,但笔者认为,删除冗余条件,会使得没有注释的情况下代码的阅读更加困难。

  在加锁的方式上,笔者全程使用synchronized关键字,虽然很稳,但是缺少了对其他方式的体验和了解。如果不是涉及到作业分数,体验更多的方法可能会更有收获。除了物理锁,也从各种途径了解到了各种逻辑锁的设计,但是缺乏实践。

  在抽象层次设计和设计模式运用上,还是有很多遗憾。这个单元的架构参考了生产者-消费者模式,但是由于架构的设计,对于很多同学使用到的观察者模式,笔者并没有需求。主要的遗憾是在抽象层次上,除了线程必须继承的runnable接口,再无接口的使用,同时继承的设计。特别是作业3的3种类型的电梯,实现前没有完整的想法,只有过程化的构思,导致没有办法抽象出一个超类。虽然说并不会涉及到使用超类的容器进行统一的电梯管理,但是在考虑到代码的复用性,这样做还是很有问题的(写完以后发现3类电梯大部分代码都是相互复制的,而且检查进出人、检查换乘、检查转向等方法的划分,使得其中许多方法在三种电梯中是一致的,完全可以通过在超类中定义)。

  关于性能与优化,这单元的作业,尤其是后两次的作业,对于笔者来说,高性能的思路非常难确定。很多策略之间不存在绝对的性能碾压,在不同倾向的数据下(具体涉及到请求的密度,请求的种类分布等等),性能互有优劣。因为不知道数据集的具体情况,可能你认为的优化其实不是优化。这里笔者有一个很典型的经历:最后一次作业笔者曾经尝试将新增的电梯由LOOK转为SCAN,在自己构造的测试中,10组数据有7组是变快了,但是中测样例中涉及到新增电梯的几乎每一组数据都变慢了。此外,部分非自由竞争的调度模式,可能会被针对性地捏造数据攻击,即出现大部分时候都有着很好的性能,但在极端情况下却会TLE。总之这个单元的作业,性能优化是一个涉及到很多trade-off的过程

  关于测试和调试,前两次作业完成了时间监测以外的全部评测,第三次由于时间原因,只对换乘目标达成和电梯基本逻辑进行了检测。但是由于对命令行和Python了解有限,仍然没有实现自动投放数据的评测模式。同时,从最近一次的研讨课上了解到,可以利用IDEA内置的单元测试功能完成类似testbench的测试文件,对单个类的功能进行测试;此外,可以利用java中的内置类ThreadInfo等,完成对CPU使用时间,锁的持有情况等进行监测;debug logger也是一种不错的调试方式。

  

 

Guess you like

Origin www.cnblogs.com/xmy1225/p/12701897.html