其他分享
首页 > 其他分享> > OO电梯作业总结

OO电梯作业总结

作者:互联网

(一)第五次作业

一、设计思路

生产消费者模型,输入接口是producer,调度器是tray,电梯是customer。由于只有一架电梯,所以生产消费模型满足以下条件:

  1.  一个生产者,一个消费者
  2. 托盘不为空时,消费者可以取走请求。任何时候,生产者可以添加请求。(托盘无容量限制)

互斥:生产者——生产者,消费者——消费者。

同步:生产者至少生产一次,消费者才能消费。

互斥问题我给get和put函数加了方法锁。

二、调度策略:FAFS傻瓜电梯

遵循先到先服务,所以调度器作用很小,只是完成托盘的功能。调度器内部有一个请求队列,还有put和get方法。输入接口和电梯是两个线程。输入接口每收到一个请求,即时加入到调度器队列里。电梯每执行完一个请求,向调度器索要下一个请求。

三、类图

四、度量分析

 

 

五、Bug分析

如何结束电梯线程?

调度器里面有一个close变量标志是否收输入结束了。输入接口(使用put函数)传入为null的请求时,调度器会将close变为有效。电梯执行完最后一个任务,get下一个请求。如果此时请求队列为空,且close有效,电梯也结束线程。

 

但最开始,我把结束电梯的条件写错了,只要close有效就会结束。但可能还有请求没来得及读进调度器队列。正确的get函数写法是,如果请求队列为空且close无效,说明输入没有结束,还要等待后面的请求,就先wait。不再满足wait条件的时候,如果队列仍为空且close变为有效了,就结束电梯。

 

第二个问题是,输入结束时,我没有调用put方法,而是单独调用一个end函数改变close状态。这样会有一种情况:最后一个请求还在put函数里没有进入请求队列,但后面被紧接着调用了end函数把close置位了,此时也是请求队列为空,close有效,最后一个请求就没有执行。解决方法是,结束输入时,依然调用put函数,传入一个为null的请求。Put函数有synchronized修饰,必须一次一次调用。

六、互测bug

此次作业比较简单,没有发现互测的bug。

 

(二)第六次作业

一、设计思路

仍然是生产消费者模型,与第一次作业类似。

二、调度策略:ALS可捎带电梯

在调度器里增加了一个flush函数。电梯用get方法得到第一个请求,称为主请求。在去主请求fromfloor的过程中不捎带,从from到tofloor时,每到达一层,用flush函数向调度器索要可以捎带上的请求。捎带条件是,捎带请求与主请求同方向,且捎带请求的fromfloor在电梯当前floor到destination之间。Destination的初值是主请求的tofloor,在电梯每次收到捎带请求的时候更新为主请求和捎带请求tofloor里面更远的那个。

一开始我的电梯在到达主请求fromfloor的时候也可以捎带,但是捎带条件极其复杂,使用的不是destination,而是highestFromFloor,highestToFloor这些变量作为运动的终点。考虑到对第三次作业的可拓展性,我放弃了复杂的捎带策略,但也因此没有拿到性能分。

三、类图

 

四、度量分析

 

五、bug分析

从第一次作业直接移植进出电梯的方法,出现了同一个人进两次电梯的问题。所以我在电梯类里面设置了两个请求队列,一个是还没进电梯的请求,一个是在电梯里面的请求,解决了这个问题。

如果在0秒出现两个从1层进电梯的请求,我最初的写法不能捎带上第二个请求。在电梯刚到达一层,和开门之后都向调度器索要可以捎带的请求,解决了这个问题。

六、互测bug

没有发现同组的bug。调度策略越慢,越不容易出现bug,传统的捎带策略最保险。

 

(三)第七次作业

一、设计思路

在调度器里,给每个电梯设置一个队列。输入接口传入一个请求的时候,调度器根据分配策略分配给一个电梯队列。

二、调度策略

调度器分配请求的原则是,优先分配给能直达的电梯,都能直达的时候优先级A>B>C。否则调用transfer函数,进行换乘分配。

电梯从调度器属于自己的队列里取请求,和查看捎带请求。

 

关于换乘

将换乘请求分成两个,分配给两部电梯。

其中一个难点是,确保完成换乘的第一站,再开始第二站。我新建了一个TransferRequest类保存换乘请求的id,负责第一站的电梯,第二站的电梯。将一个换乘请求分配好,就设置一个对应的TransferRequest,存入换乘队列。当完成第一站请求(即人走出电梯)的时候,把它从换乘队列删除(transfered函数),执行第二站的电梯检测到队列里没有这个换乘请求了(检测函数为transfering函数),才能执行第二站。

三、类图

 

四、度量分析

 

五、bug分析

我设计初期的bug围绕着一个问题:电梯get第一个请求的时候,何时wait,何时结束运行?

Wait的时机是,电梯的请求队列为空且输入没有结束,或者输入结束电梯请求队列不为空但所有都是没完成第一站的换乘请求。相对的,每次输入新请求和完成换乘第一站的时候,都要notify。

结束运行的时机还是,请求队列为空,且输入关闭。

六、互测bug

最常见的问题是多步电梯造成的线程冲突。有些同学让三部电梯同时从一个请求队列里抢请求,多个函数都会修改请求队列,但没有严格互斥。除了请求队列,三部电梯还有其他共享数据,对所有共享数据访问的时候产生混乱。

 

(四)个人总结

我发现电梯的调度策略越简单,可拓展性就越强,从第二次到第三次作业,我写的非常快,因为捎带的调度策略完全不用修改,电梯类只增加了载客量,速度这两个属性。

但是如果想达到比较有效的优化,就要从LOOK模式转变成SCAN模式,工作量指数型增长。

 

SOLID原则:

  1. 单一责任原则:在电梯类里,有goUp,goDown,openDoor,closeDoor,peopleMove函数符合单一职责原则,但是operate函数在while循环里反复调用这五个小函数,条件比较复杂,显得臃肿。
  2. 开放封闭原则:作业二到三的扩展很方便,电梯的载客量和速度作为一个属性,增强了扩展性。
  3. 里氏替换原则:第三次作业里面A,B,C电梯是电梯类的实例,避免大量复用。但是由于电梯作业结构简单,三次都没有用到继承。
  4. 接口分离原则:没有用到接口
  5. 依赖倒置原则:调度器里有电梯的引用,电梯里有调度器的引用。

标签:OO,请求,队列,作业,调度,捎带,电梯,函数
来源: https://www.cnblogs.com/mollygarden/p/10745813.html