作业5: 多线程电梯
一开始构想的设计是, 输入一个线程, 3个电梯各一个线程, 调度器一个线程。 输入线程专门处理输入请求, 将合法的请求加入请求队列里。 这次由于是模拟的真实的时间, 同质请求可以直接在输入的时候就给判了。 调度器就是不断的扫描请求队列, 对每一个没有分配的请求看看有没有电梯可以给。 电梯线程就是一个自动机, 自己按照设定的状态运动就行了。 然后就是考虑程序如何结束的问题, 输入控制线程很明显当输入输入结束标志时可以结束, 调度器在输入结束后且请求都分配完毕后可以结束, 电梯就在调度器结束且没有请求分配给它后就是。 设计的大概模式没有什么问题。 后面遇到了一些细节上的问题, 主要是多线程调度的不确定性, 还有就是时间误差的积累。 请求多了之后发现运算的时间误差积累得大了起来可以影响输出了, 于是就在电梯里预存了一个时间, 每次要sleep个3s或是6s的时候,用存的时间算出要sleep到什么时间点, 再更新一下存的时间, 抹掉一些运算误差, 效果好了不少。
从类图上来看的话请求类由于有各种各样的属性, 相应的方法也很多, 看起来十分庞大, 考虑到分工的问题还可以在细化一下这个类。 从Metric度量上来看, Ctr_new类中的run方法圈复杂度高, 这个类主要是继承上一次电梯的调度器, 基本保留上一次电梯的设计。 Main类中的print方法参数个数高, 这个主要是考虑到多线程的输出, 给控制台输出专门写了个函数放在 Main类里面, 加上了synchronized, 由于各个线程的输出需要的参数不同, 便直接合并在了一起, 其实有很多是用不上的, 可以考虑分割成几个方法。 Elev类的run方法块嵌套深度高, 考虑细分这个函数的职责, 同时避免重复。
这次多线程电梯自己还算写得比较满意的, 公测互测都没有挑出bug。 互测别人都时候就是自己构造小数据, 具体还是测试一些功能上实现是否完备。 由于多线程上答案有许多种, 数据大了的话也不好查看正确性。 对面这个人的代码测某一个大数据的时候发现有点问题, 试图找到问题的所在, 把数据缩小后发现又对了, 看代码也找不出什么原因, 也就没算他错了。
作业6: IFFFT
这次作业的设计思路很简单, 重点学习到的是各种对File类的操作。 主要的线程就是一个请求输入对应一个监控器线程。 直接在Main函数中处理完输入, 根据合法输入请求的个数, 依次打开各个监控器, 监控根据监控模式, 以某个频率保存监控范围内所有文件的信息, 通过比较相邻两次信息的不同点, 根据监控模式作出是否相应。 这一次关于线程结束就是自定义了个让测试者结束所有线程的方法, 因为毕竟监控一个文件本身是没有结束条件的。
从类图上看, SafeFile类比较大, 主要是对于File类的线程安全包装, 这个可以理解。 Moniter类属性过多。 从Metics度量上来看, Moniter类的run方法圈复杂度和块嵌套深度都很高。 这个run方法几乎是整个程序的核心部分, 监控器的所有判断, 扫描, 响应工作全都放在这里了, 写得十分面向过程, 重复的代码段也相当多, 我自己也觉得这一段很难看, 可以考虑更加细化分工, 切出几个方法或是新建几个类来分担一部分职责。
关于自己的bug的话, 自己程序本身没有什么问题, 就是测试线程的布置问题, 由于输入不是专门写了个线程, 可能会阻塞测试线程, 所以测试线程只能在输入之前开, 被算了个bug。 别人的代码的话, 做了些简单的功能性测试, 对面也只是犯了个非法情况的小错误, 我也发现了其原因。 如今对于多线程的测试, 由于多线程在执行过程中有偶然性, 我都会找到具体的bug后再从代码上分析具体的原因才往上报。 感觉自己也学到了一些东西,避免犯类似的错误。
作业7: 出租车调度1
这次设计思路和多线程电梯很像, 而且由于有了gui, 很多东西可以用它自带的。 首先输入控制器一个线程, 处理请求的输入, 将合法的请求加入请求队列中。 调度器扫描请求队列, 为每个窗口期内的请求筛选合适的出租车发起抢单, 为窗口期结束的请求筛选合适的出租车进行派单。 100辆出租车一辆是一个线程, 每辆出租车根据当前所处状态按次序行动。 主要的交互就是在于出租车自己是个自动机, 会改变自己的状态, 调度器选定出租车派单, 会从外界改变其状态。 时间误差上沿用多线程电梯的方法, 将误差缩到几乎可以忽略。 对gui中的bfs进行了改进, 将最短路的计算放在了程序初始化, 预处理获得, 较少运行过程中的运算时间。
这次自己犯了个严重的错误, 就是处理输入的时候, new了一个局部变量, 此后所有的输入都是覆盖性的写入, 创建新的请求又是直接把这个局部变量穿进去, 由于java中的这些变量其实都是指针, 导致了请求不断被覆盖, 被测试者弄了3个bug。 自己测试的时候小数据都是间隔很久再输入, 大数据直接另写一段代码直接加的, 就没有发现, 也是吃了个大亏。 给对面测试的时候, 由于公测很少, 就只是一些非法情况, 给对面首先是读代码, 发现了一个功能实现与指导书不符的, 然后试了一个随机大数据压力测试, 发现结果差得很远。 设计原则上没有细究,(本来想加一个显式表达原则, 心太软还是算了。
心得体会
- 线程安全方面主要是多个线程访问同一个数据的时候, 有读有写, 还有多个线程执行IO操作的时候。 要注意用synchronized的加锁来限制, 不过也不要加太多, 影响效率。
- 设计原则方面, 自己主要考虑的还是责任的均衡分配。 把每个类的职责明确一点, 这样的代码看起来清晰, 维护起来也好维护。 显式表达, 懂我原则也是习惯了, 变量名命名有规可循, 自己心里有个数。 重用原则也是自己一直在注意的, 这样可以减少代码的冗余。 其他的原则在今后的学习中也会逐渐习惯成自然。