OO第二次课程总结分析

  前几次的作业都是单线程的,总体来说和以前的思维模式和调试等存在着一定的挂钩,在设计上整体难度还不算太大,这次开始了多线程编程,难度可以说是质的飞跃,构思上所考虑的不止一点两点,在整体的基础上还要考虑线程的同步安全等问题,下面针对三次作业的分析来谈谈在多线程编程上所犯的错误和得到的收获。


一、多线程电梯

1.设计策略

作为多线程的第一次作业,又恰逢清明假期,可以有相对充足的时间来学习多线程的相关知识和进行构思(可以说这个清明假期过得非常揪心了),因为有了前面两次电梯的积累,这次关于同质和捎带的问题并未花太多时间(但是考虑前面的代码可塑性太差,这次就进行了重构,但核心思想没变),大部分时间用在了构思三部电梯的调度和安全问题上。多线程的设计与单线程不同,在构思实现逻辑的同时还要决定开多少个线程,每个线程之间的交互影响、线程安全问题等,复杂程度大大提升。在经历了多天的思考之后,我共设计6个线程(分别为主线程(Main)、输入线程(Inputhandler)、调度器线程(Scheduler)及三个电梯线程(Elevator))。共有4个请求队列(一个总的,3个各自电梯的)。

各个线程的工作如下:

  • 主线程(1):其他线程的实例和启动
  • 输入线程(1):在收到输入结束符之前持续接受控制台输入,将有效请求(其中无效和同质都不算有效)放入请求队列
  • 调度器线程(1):当总的请求队列不为空或输入线程未结束时,不断扫描总的请求队列,根据三部电梯状态和分配原则来将请求分配给合适的电梯
  • 电梯线程(3):当自己的请求队列不为空或调度器线程未结束时,不断扫描自己的请求队列,根据电梯状态和请求来执行相应的动作

  关于线程同步控制的考虑,因为请求队列会涉及到加入请求、删除请求、从队列中取出请求及获取队列大小等操作,这都是线程不安全的,因此在对队列的操作上我都上了锁,同样,电梯状态会有改变和获取的操作,同样都加了锁。

2.程序结构分析

度量分析

OO第二次课程总结分析

类图

 OO第二次课程总结分析

sequence diagram

OO第二次课程总结分析

  个人觉得整体架构还算满意,各个线程分工较明确,只是在具体实现时有部分代码显得臃肿,还未能做到方法功能单一,部分方法用了太多的条件分支,在代码重用性方面有些欠缺,代码的可塑性不强。

3.bug分析

  公测有四个样例没过,主要还是栽在了调度线程的问题上,分配请求的方法发生了错误,在判断电梯运动量上未做到安全,考虑不够周全,导致多个点爆了,另外时间永远是一个头疼的问题,公测终究还是被报了时间的错误。互测被找了一个格式错误,对格式边界的把握还是不够。

   测他人时还是先从格式下手,再测一些基本功能的实现(先观察代码看必要的地方有无加锁,再针对性测试),然后从边界情况来找到是否符合预期,最后就是用大一点的数据去测。


二、IFTTT

1.设计策略

  老实说,这次作业的难度比上次的要大,毕竟上次的作业有之前的作业的积累,并且给的时间相对较长,但这次作业不仅时间相对少,还涉及到文件系统,光是理解作业需求就花了挺长时间,导致最后作业完成的很糟糕。这次的构思基本就是针对一个作业开一个线程,每个线程监控每份作业相应的某个触发器和某个task,并且在创建线程的开始就为每个作业建一个状态快照,线程实时扫描文件,并实时生成更新快照,比较前后快照的不同看是否触发了相应的触发器,根据相应的触发器而做出相应的动作。各个线程如下:

  • 主线程:处理输入,如队列,线程实例和启动
  • Detail线程:处理detail这个任务
  • Recover线程:处理recover这个任务
  • Summary线程:处理summary这个任务
  • TestThread线程:提供测试接口
  • Watch(监控)线程:根据触发器不同让哪个任务执行

  关于线程安全的考虑,对文件的操作会导致线程不安全,因此这块利用锁来实现同步,保证同一时刻只能有一个线程在对文件操作,避免应线程不安全而导致难以预料的错误。

2.程序结构分析

度量分析

OO第二次课程总结分析

类图

OO第二次课程总结分析

sequence diagram

OO第二次课程总结分析

  这次的程序可以说写得很糟糕,由于时间的不足,没有考虑监控目录的情况,并且程序的主线程写了太多,显得很臃肿,应该将输入处理等移出,主线程只负责启动其他线程方为上策。同时,Safefile类忘记提供获取最后一次修改时间等方法。总之,这次的构思有些问题,程序结构设计的不是很合理。

3.bug分析

  这次互测被找的bug是最多的一次,因为没有实现目录的监控,这部分被找了bug,同时,recover也未能实现,导致被测了许多bug,也是对自己的反思吧,毕竟自己没有实现那部分的功能。

这次互测就是针对几种触发器和几种任务的组合,一一进行几组测试,从而找出与预期结果相悖的点,针对找到的点,找到对应的代码,看关联的方法或其他,再针对地找其他bug。


三、出租车

1.设计策略

  因为出租车是相互独立的,互不影响,故考虑每辆出租车一个线程,每辆出租车都有各自的私有属性,都能进行抢单、运送等动作,且出租车之间没有交互,各自独立。同时因为每辆出租车要从抢单出租车中选出合适的,故每个出租车都设了一个属于自己的请求队列,调度器负责为乘客选择合适的出租车,出租车负责将乘客送往目的地。因此,程序的线程如下:

  • 主线程:读入地图信息、声明静态常量、线程实例及启动
  • 输入线程:输入请求并将有效请求(无效、同质均不算)加入队列
  • 出租车线程(100):出租车的各种运行(随机跑或按最短路径跑)
  • 调度器线程:选出抢单出租车并进行派单

  关于线程安全的考虑,本次总的请求队列是一个共享资源,故是非线程安全的,在对队列的操作中都加了锁,由因为调度时要获得出租车的状态、信用度即位置等,所以在出租车的状态、信用度及位置等的set,get方法上均加了锁。

2.程序结构分析

度量分析

OO第二次课程总结分析

类图

OO第二次课程总结分析

sequence diagram

OO第二次课程总结分析

  这次程序的方法我尽量地去写得很短,只实现某一个功能,其他部分各人认为写得算是比较简洁,但调度器的run方法还是用了太多的循环和条件判断,导致度量变红,这一块应该改进,抢单可以另外用一个方法实现,而不用写在run里还有Taxi的判断走哪条路的方法复用性不够强,代码的相同地方过多,可以通过传目的地参数的方法进行此部分代码的复用。

3.bug分析

  这次交得急,没有写测试接口,被报了一个设计缺陷,同时因为gui中算最短路径的方法耗时太长,在输出时我用的是假时间,但gui上跑得较慢,因为这个原因,接单和服务状态下车要比等待时跑的慢,被测试者找了bug,还有因为在遍历数组时条件判断不够充足,导致程序在一定间隔内输入的多条请求引发NULLpoint的crash。

  这次找bug为了能保证出租车一定能接到单,我找的时候让出租车初始位置从某个点出发,便于进行测试。


最后的心得体会

  经历过这最痛苦的三周,对多线程编程也有了自己的体会。在多线程电梯刚开始使用锁时,傻傻地锁住了sleep,导致线程之间不能并行,这个让我困惑了许久,后来逐渐缩小锁的方法的范围才解决。多线程的运行机制比单线程复杂得多,在动手之前要足够了解这种机制,不然真的发生了预料之外的后果你也会手足无措,所以事先设计构思、想好哪些资源需要共享,哪些需要同步,哪些线程是独立的,哪些是交互的,我认为这种考虑在多线程编程中是非常有必要的。

上一篇:php 微信推送消息


下一篇:hyperledger fabric 中java chaincode 支持离线打包