1. 输入条件的bug
这周OO第二次多线程电梯作业,我这次吸取了上周的教训,周三开始动工,周四就搞定,并顺利通过本地和网站的自动测评机的正确性测评。
在周四的自动化测评中找出如下bug:
- 电梯进程的结束条件错误: 我原先定的结束条件是input进程结束并且当前电梯进程的pin和pout队列为空。但是这用做可能会漏掉最后的输入,因为当最后的输入p被添加到全局pout队列,然后input进程结束。若这时候电梯的pin,pout队列恰好为空,而在controller进程把p添加到电梯的pout之前,电梯进程就结束了,然后p被添加到电梯的域pout(因为进程虽然结束,但是电梯这个实例还存在),然后p就永远不会被搭载。
我吸取的经验是:若多线程的多个线程的输入之间相互依赖: Aout -> Bin,
Bout -> Cin。 那么B结束的条件是A结束且B处理完当前数据; C结束的条件是B结束且C处理完当前数据; C不可以跳过B而已A的结束作为结束条件。
2. 代码处理数据的逻辑的瑕疵
之后笔者在周六用小黄鸭测试法检测了一遍代码,找出了代码逻辑中关于maxfloor和minfloor取值的小瑕疵,修改后代码性能更优。
又找出了二次assign算法的瑕疵,将第二次分配调整为以distance为度量标准,优化了性能。
3. 线程的同步互斥保护
我观察了在网站上我的反馈结果和本地测评机的结果,发现我的电梯的上下楼时间和开关门时间远远大于0.4s. 最后通过print大法, 发现是我将elevator的sleep放在了synchronized代码块里。导致长其中某个elevator长时间占有锁,这个elevator结束后,另一个elevator又长时间占有锁,导致某些需要锁来执行指令的elevator长时间得不到锁。
4. 多次提交网站发现的bug
我多次提交网站后,发现我WA了。原因是我先start了controller线程,这里面要调用elevator的addPout方法,但addPout方法加了controller的锁。但我是在start Controller后面才为elevator设置了Controller实例域,因此执行到contrller run里面的elevator.addPout时,就可能报空指针错误(锁不能为空指针)。因此,尽量现在开头把各种实例的实例域都设置好,最后再start线程。
5. 总结
-
小黄鸭测试法非常重要,一定要做;小黄鸭测试法和性能检查和线程安全都要在周五之前完成检查,这样才有时间修改
-
观察本地和网站输出结果的时间间隔
-
print大法
-
避免将sleep,join和长耗时的计算放在synchronized代码块里(可以设置标志位将这些耗时的代码移到监控区外),因为这样的话执行这个代码块的线程会长时间持有锁,导致其他进程都处于阻塞或wait态;wait可以放在synchronized代码块里,因为wait会交出锁。
-
一个好的多线程程序,在线程overview里,它的running进程数和waiting进程数一定是平稳变化的; 若进程数变化杂乱无章,极有可能线程与monitor有问题。
-
尽量现在开头把各种实例的实例域都设置好,最后再start线程。