通过了这一单元,对于多线程有了些许理解,在此做个博客,既是完成作业,也是方便以后复习。
三次作业设计策略
三次协同和同步控制均采用 lock
和 condition
的模式,因为较为灵活,而且能约定时间,比较简单灵活,不易产生死锁。以下是简单介绍。
-
第一次
首次写多线程程序,以ddl为动力,以讨论区为资源,不断地探索与思考之后灵机一动,写了个带bug的小程序,采用sstf调度算法,仅主线程和一个电梯线程,两个线程交相进行,但尽管只有两个线程,还是出现了bug,惜得89。
-
第二次
看起来难度好像不大,依旧每个电梯单独为一个线程, 新增电梯调度器这个线程,依旧为sstf,调度器找最近的电梯来接人,为了锻炼自己的能力,沒有瞎锁,只在必要地方lock,基本没加什么锁,采用肉眼观察法验证了正确性后,交上去过了中测就没管了。但人看不如机测,互测被hack后,写了评测机,开测后果然自己是错误最多的,有时候判断不出来人满,强行连载十数人,令人叹为观止。自然强测炸了,错了四个数据点,惜得78。
-
第三次
前两次的失败,让我感觉该放下OS,好好OO了,由于需要换乘,在Person类中直接static了一个大策略,即从[-3, 20] -> [-3, 20]的策略都直接自动生成了,而且每个有多种策略,每个策略使用不同的电梯,每个策略都是这个或这两个电梯的最小时间策略,最多使用两种电梯。如下所例:
18 -> 1: 18 -> 1 in A, 18 -> 15 in A 15 -> 1 in B, 18 -> 15 in A 15 -> 1 in C
之后通过
public void setRequests(Boolean[] busys)
这个函数根据当前各类电梯的空闲程度对每个乘客选择合适的策略。之后每个电梯自行调度,public static long getTime(args)
得到上行或下行满足所有乘客需求所需要的时间,选择时间最少的方向,通过递归来实现。 又写了个评测机,七种模式来随机,但还是没能测出我的bug,因为我的评测机是不管cpu实际运行时间的,但在互测被hack16次,有两处破绽:一个是由于电梯选择策略是static块的初始化,导致第一次用的时候才开始初始化,不在一个线程容易出事。还有一个是在一个人所有策略都行不通时,会标记而且把它放在队尾,让下一个人上,即 remove 后 offer,可惜,我用的是
PriorityBlockingQueue
,会自动排序,导致一直轮询,cpu_run_time_limited,所幸强测机也没测出,幸得99。
第三次作业SOLID分析
Single Responsibility Principle
![](https://images.cnblogs.com/cnblogs_com/Wandy666/1715429/o_2004160401210AD495202A428D1DFAA57431A575F841.png)
如上图所示,每个类的作用相当单一
Open-Closed Principle
![](https://images.cnblogs.com/cnblogs_com/Wandy666/1715429/o_200416040153VYQ4DH]EUG(B5]MT$)YU@[F.png)
![](https://images.cnblogs.com/cnblogs_com/Wandy666/1715429/o_200416040209123.png)
由于使用了一个TypeElevator 这个枚举类,如果所有类型的电梯使用同一种方法,如果增加电梯功能仅需在Elevator加一个方法即可,如果增加电梯种类只需在TypeElevator中加入相关数据。
Liskov Substitution Principle
无继承,pass
Interface Segregation Principle
无接口,pass
Dependency Inversion Principle
???,pass
度量分析
TypeMetrics
Type Name | NOF | NOPF | NOM | NOPM | LOC | WMC | NC | DIT | LCOM | FANIN | FANOUT |
---|---|---|---|---|---|---|---|---|---|---|---|
Elevator | 12 | 2 | 19 | 8 | 316 | 56 | 0 | 0 | 0.315789474 | 0 | 0 |
ElevatorController | 6 | 0 | 11 | 7 | 189 | 35 | 0 | 0 | 0.181818182 | 0 | 0 |
MainClass | 3 | 2 | 2 | 2 | 33 | 6 | 0 | 0 | 0 | 0 | 0 |
Person | 5 | 0 | 22 | 20 | 213 | 47 | 0 | 0 | 0.318181818 | 0 | 0 |
RequestOfPerson | 4 | 0 | 10 | 9 | 53 | 13 | 0 | 0 | 0 | 0 | 0 |
SimpleRequest | 3 | 0 | 6 | 6 | 25 | 6 | 0 | 0 | 0 | 0 | 0 |
嗯,虽然看不太懂,但是感觉不错。
MethodMetrics
Type Name | Method Name | LOC | CC | PC |
---|---|---|---|---|
Elevator | compare | 8 | 2 | 2 |
Elevator | Elevator | 12 | 1 | 3 |
Elevator | run | 39 | 6 | 0 |
Elevator | finished | 10 | 1 | 0 |
Elevator | arrive | 9 | 2 | 1 |
Elevator | updateDes | 28 | 10 | 0 |
Elevator | getTime | 64 | 11 | 5 |
Elevator | goOutElevator | 5 | 2 | 1 |
Elevator | judge | 8 | 2 | 3 |
Elevator | goInOut | 7 | 2 | 0 |
Elevator | OpenDoor | 16 | 1 | 1 |
Elevator | goIn | 16 | 2 | 1 |
Elevator | goOut | 35 | 4 | 1 |
Elevator | goOutPerson | 9 | 3 | 0 |
Elevator | goInPerson | 9 | 3 | 0 |
Elevator | isFull | 9 | 1 | 0 |
Elevator | getlocation | 3 | 1 | 0 |
Elevator | addPerson | 10 | 1 | 1 |
Elevator | isFinished | 3 | 1 | 0 |
ElevatorController | ElevatorController | 25 | 3 | 0 |
ElevatorController | compare | 3 | 1 | 2 |
ElevatorController | run | 36 | 5 | 0 |
ElevatorController | elevatorFinished | 10 | 4 | 0 |
ElevatorController | pushToElevators | 45 | 8 | 1 |
ElevatorController | allHasWaiting | 8 | 3 | 0 |
ElevatorController | isFull | 13 | 4 | 0 |
ElevatorController | getLock | 3 | 1 | 0 |
ElevatorController | getCondition | 3 | 1 | 0 |
ElevatorController | addPerson | 12 | 2 | 1 |
ElevatorController | addElevator | 26 | 3 | 1 |
MainClass | main | 25 | 5 | 1 |
MainClass | isFinish | 3 | 1 | 0 |
Person | reverseRequests | 7 | 2 | 1 |
Person | printBase | 15 | 5 | 0 |
Person | hashFloor | 3 | 1 | 2 |
Person | fillList | 6 | 2 | 4 |
Person | getMethod | 40 | 12 | 3 |
Person | Person | 6 | 1 | 1 |
Person | breakDownRequest | 3 | 1 | 0 |
Person | regFloor | 8 | 2 | 1 |
Person | setRequests | 20 | 4 | 1 |
Person | arrive | 4 | 1 | 0 |
Person | getFromFloor | 6 | 2 | 0 |
Person | getToFloor | 6 | 2 | 0 |
Person | getType | 3 | 1 | 0 |
Person | getPersonId | 3 | 1 | 0 |
Person | recover | 6 | 2 | 1 |
Person | isWaiting | 3 | 1 | 0 |
Person | notWaiting | 3 | 1 | 0 |
Person | ifWaiting | 3 | 1 | 0 |
Person | isInElevator | 3 | 1 | 0 |
Person | goinElevator | 3 | 1 | 0 |
Person | gooutElevator | 3 | 1 | 0 |
Person | getDes | 6 | 2 | 0 |
RequestOfPerson | RequestOfPerson | 6 | 1 | 2 |
RequestOfPerson | RequestOfPerson | 6 | 1 | 3 |
RequestOfPerson | addRequest | 3 | 1 | 3 |
RequestOfPerson | clone | 3 | 1 | 0 |
RequestOfPerson | toString | 7 | 2 | 0 |
RequestOfPerson | reverse | 7 | 2 | 0 |
RequestOfPerson | getSimpleRequestNow | 6 | 2 | 0 |
RequestOfPerson | finishSimpleRequest | 3 | 1 | 0 |
RequestOfPerson | hasFinished | 3 | 1 | 0 |
RequestOfPerson | getState | 3 | 1 | 0 |
SimpleRequest | SimpleRequest | 5 | 1 | 3 |
SimpleRequest | getFromFloor | 3 | 1 | 0 |
SimpleRequest | getToFloor | 3 | 1 | 0 |
SimpleRequest | getType | 3 | 1 | 0 |
SimpleRequest | toString | 3 | 1 | 0 |
SimpleRequest | reverse | 3 | 1 | 0 |
CC高的几个都是和优化算法有关。
类之间仅有相互调用的关系,类图没啥用。
BUG分析
已经在设计策略分析了。总之,用了 lock 和 condition,很难有死锁,但是还是要仔细考虑同步问题。
分析别人BUG
评测机并发测试一千例,subprocess模拟输入,wait(210),超过就是TLE,加上 check.py 自己写了几个类模拟电梯运输并且判断了时间,除了轮询测不出来真实cpu时间,其他很稳。数据生成有七个类型,针对不同楼层,不同电梯类型,以及不同的时间间距,但随机性还是太大,不太好,但还是测出来不少bug。
心得体会
对多线程运作更加了解了,有一个好的设计可以减少很多需要用锁的地方,不要暴力全加锁。