OO第二次博客作业
OO第二次博客作业
第五次作业分析:
(1)设计原则:
这是第一次使用多线程来编写程序,开始编写之前,整理清楚自己前两次作业的作业框架结构之后,按照PPT给的框架,设计了一个三种线程共享2项共享资源的方式,即输入线程与处理线程共享总请求队列;处理线程与三个电梯线程共享每个电梯的请求队列。输入线程只负责读入请求进入总请求队列,在输入线程对总请求队列写入的时候,对总请求队列加锁,使得不会产生数据冲突。处理线程的任务是将当前的总请求队列中符合条件的请求分别派给三个电梯线程的分请求队列中,在分派的过程中要对总请求队列和电梯分队列加同步锁。电梯线程执行分请求队列中的请求,在执行请求的过程中,需要实时对分队列进行处理,故也需要加同步锁。
(2)度量分析:

>代码中继承了ALS电梯的部分代码,主要的线程交互在于调度器,输入处理和电梯之间

>与之前的代码问题类似,还是没有考虑到部分类过长而且部分方法功能过于复杂的问题,问题主要出现在调度器类中,将总队列拆分成分队列的代码复杂度较高,代码也比较复杂。
(3)程序的bug:
在公测和互测中,没有被检查出BUG。但是在测试程序的时候,发现了两个小BUG,就是在处理系统时间的时候,对于输入的请求时间是先处理再转成double类型,使得小数点后被忽略;另一个BUG说小不小,在指导书给出的测试样例中,不同的运行状态对应了两种不同的运行结果,程序最终结束的时间分别是138s和144s,究其原因是两个本来应该同时结束的电梯线程由于多线程的随机性,结束的时间并不相同,导致了运行结果的错误。在其他同学书写的使用了假时间或者时间系统的程序中,并不会出现这样的问题,这说明如果在实际操作中,没有写好,多线程可能并不能比单线程来的好。
第六次作业分析:
(1)设计原则:
与第五次作业相同,本次作业仍然是多线程程序。在这次作业中,与之前不同的是,这次的输入决定了线程的数目。输入时,将所有需要监控的操作统一读入,对每一个操作开辟一个监听线程,最多可以有100个监听线程同时运行。这些监听线程,用来监听文件操作是否满足要求,如果触发监听条件,那么触发相应的动作。如果触发summary_log,那么调用summary_record线程进行写入summary文件,如果触发detail_log,那么调用detail_record线程写入detail文件,如果触发的是recover,那么更改为原来的状态。在线程安全上,由于没法在文件系统上加锁,所以我解决让文件扫描和文件操作不同时进行的问题,只能在测试线程中要求对文件操作需要等待一定的时间;但是对于两个记录文件,需要加同步锁保证同时只有一个监听线程可以获取文件的写入权。
(2)度量分析:

>trigger触发器可以再次拆分,这样每个trigger的功能需求就更明确

>trigger类中功能包含了三种触发器的功能,功能上过于繁重,且代码量也较大,应该差分成三个类
(4)程序bug:
这次代码的互测者并没有测试我的程序,算是捡了个漏。但是在设计上还是出现了很多年的缺陷,比如,如果一个目标在重新命名之后,如果出现了两个线程内容相同的时候,这两个线程虽然是相同的操作,但是监听的行为却有了两个相同的。在写代码的过程中,因为转换太麻烦我对这个不正常的行为进行了README,实际上,应该在运行过程中实时对比每个线程之间的类型,从而规避此类问题。
第七次作业分析
(1)设计原则:
在本次设计中,共设立了三个线程(不包括主线程),与电梯作业类似,输入分为输入线程,调度线程和处理线程。输入线程和调度线程共享请求队列,与电梯中的分配不同的是,在本程序中,调度器扫描当前请求队列中的所有请求,为每个请求寻找适合的出租车,并将这条请求交给出租车来执行,在处理线程中,处理的是一个由100辆出租车组成的出租车集合线程,采用状态机变化来实现出租车状态的转化。同时,使用了课程组提供的GUI界面,也作为一个独立的线程。线程安全上,与之前类似,对所有冲突的资源,即请求队列和出租车列表进行了同步加锁。
(2)度量分析:

>在程序开始设计的时候,没有考虑到出租车对象的成员参数,导致后来又重新添加,很乱

>在更新出租车队列的时候的更新函数使用了有限状态机进行更新状态,代码很繁杂。不仅如此,在调度上还因为没有注意到特殊状态的动作导致出现了异常
(3)bug分析:
公测中出现了目的地和出发点在同一个位置的测试用例,我在设计的时候遗漏了这种情况,导致在输出错误信息和更改出租车运行状态的时候,均发生了异常,原因在于出租车运行的时候是按照路径的当前最近距离减一来确定具体的位置,没有考虑到0的情况,导致接下来的计算都出现了问题。除此之外,由于我在代码中标记状态没有选择枚举类进行,不符合面向对象中的显式原则,在实际中,我也发现了使用整数进行标记,时常会忘记每个整数的含义。
互测寻找别人bug的体会:
在寻找互测bug的过程中,我首先阅读了代码,对其的架构有了一定了解之后,再进行的测试。压力测试最能暴露问题,但是由于多线程问题很难保证自己程序跑出来的结果一定是对的, 所以在压力测试中,我选择了周围同学都测试过的来进行测试。但是压力测试仅仅只能测试出程序存在错误与否,错误的具体位置并没能很好的显现出来。
除此之外,我还会根据自己在写代码过程中遗漏的一些问题进行查看,一是看被测试者有没有出现同样的问题,二是看被测试者是如何解决这个问题的,并从中吸取教训。
心得体会:
首先不得不说,多线程编程比单线程难很多,调试上也不能使用debug模式,只能使用调试输出,特定的位置输出需要知道的变量。在我以往的过程中,调试的时候习惯上打断点,执行到断点发现问题,解决问题,就是一个比较完整的debug过程。输出调试在一些特定的场合更方便,我可以定位线程在运行过程中有没有进入某个代码块,或者线程有没有被唤醒,或者这个程序的输出不正确的原因是在哪个传递过程中出的差错。输出调试可能在单线程中没有debug模式方便,但是确实是在任何情况下都可以使用的万能调试方法。
在这几次作业中,我都是优先设计自己程序的框架,包括线程数目,线程的功能,线程之间的调度关系,线程之间资源的占用情况等等。这样的设计虽然在后来都会进行调整,但是还是有很大的帮助。比如在出租车程序中,我仿照电梯的架构事先设计好了每个线程的作用,开始打算建立100个线程,线程之间分别接受调度器的调度,但是在同学之间的交流讨论中,这样的写法会导致时间上又较大的误差,所以我使用了单一线程控制100辆车的调度方法。
至此,我已经编写了三次多线程程序了。多线程与单线程相比,能提高效率,但是同时也会存在数据竞争的问题。在处理数据竞争的问题上,需要对被竞争的对象进行同步加锁,锁不可不加,但是锁加的太多,程序就会变得像单线程,丧失效率上的优势。怎么加锁合适还是需要实践。OO愉快

浙公网安备 33010602011771号