zoukankan      html  css  js  c++  java
  • 第5-7次OO作业总结分析


    (1)从多线程的协同和同步控制方面,分析和总结自己三次作业来的设计策略及其变化。


    第五次作业

      第五次作业是对多线程的初步探索,所以对于多线程的基本书写机制的认识比较多。本次作业难点在于了解多线程的运作机制,努力构建线程安全的操作方式,合理构造电梯之间、电梯与请求之间的协作关系。值得注意的是电梯、乘客在问题域中具有并发行为:

    (1)电梯之间“竞争”响应请求:也即电梯共用一个请求队列,电梯之间对请求的处理不得重复、也不得有遗漏,这对于线程安全的要求是很高的

    (2)乘客之间“竞争”使用电梯:满足捎带策略的电梯使得满足乘客需求并不是那么简单,他要求电梯根据自己的需要适当的捎带顺路的乘客。这就造成了两个问题:如何保持乘客请求队列的完整性和独立性?如何为不同的电梯选择个性化的捎带目标,同时在宏观上又对电梯有统一的调度策略?调度器(线程)如何知道电梯(线程)的状态?

      首先,关于保持乘客请求队列完整和独立的问题也就是线程安全的问题,只有线程安全处理的好才能不重不漏的取走乘客的需求,这里我们通常使用三种同步策略:线程代码加锁和中间过程on-demand加锁和共享对象加锁,经过长期的摸索,最简单易懂也不容易出错的方案是对共享对象加锁,也就是对托盘加锁。使用关键字对托盘的访问、修改方法进行锁定。这意味着共享对象不可以过于庞大,否则使用加锁的方法可能会使线程变得卡顿,时间误差拉大。同时生产者-消费者模型是必须掌握的多线程间交互模式。

      其次,关于多线程电梯捎带的个性化和统一性,我采用了和PPT上一样的设计方案,如下图所示。也即1大+3小的设计模式,大队列用于储存所有的合法请求,小队列用于放置调度器给电梯个性化选择的待处理请求。值得注意的是,三电梯的捎带和单电梯的捎带在调度器设计上有明显区别。而FR则需要一套更复杂的判断机制。比如设计重复请求函数,条件是当某个电梯处于服务状态且请求方向和目标楼层和该待判断请求一致时,该请求被判定为重复请求,应该从大队列中删除不予分配,这是很多同学容易忽略的细节。ER的判断相对简单,类似的设计只需要判断对应的电梯。调度器的策略为循环扫描大队列,将满足分配条件的请求分配到电梯,不满足的保留。保留策略是这次作业调度器设计的重点之一。

      多线程交互中的共享对象设置一个status board,电梯线程把自己的状态发布到board,调度器从中读取电梯状态。其中的get set方法是本次作业的坑点,线程不安全通常是某个线程错误读取了另一个线程未来得及修改的共享信息。修正方法是尽量把修改和访问操作整体化封装,尽量避免多线程锁的争夺强度加大,在争夺轮替间隙错误读取信息。

    第六次作业

      第六次作业的重点仍然是多线程,这里主要是对文件系统的构造和操作必须熟悉,文件操作线程安全类的封装要完备可靠,文件和目录最好是分开创建,另一个重点是快照的建立也即递归多叉树,以及基于快照的比较逻辑,这里更加推荐一个监视对象一个线程的做法,我采用的是一种监视器一个线程然后里面有监视队列,但这样因为监视器的相似性代码冗余比较多,而且监视器之间的区别也容易被搞乱,写起来效率不高。同时当文件发生移动时,修改也比较麻烦。记录本质上是一种同步行为,触发控制线程独立输出到一个内存对象(buffer),然后集中输出到外存文件的做法效率更高一些。

      这次与众不同的是我们开始接触编写代码来进行测试:程序输入不是都通过显式的命令行或控制行而是通过打靶式测试——有目的的设计输入,这样效果预计性和可复现性变得很强。

      和前几次作业的平稳落地不同,这次作业因为各种改线程逻辑,框架变来变去,最后写的非常爆炸,debug到凌晨3点也没有挽救手残的程序员写出的糟糕作品,抱着必死的信念和下次一定好好写的自我安慰交了上去,最后连各种奇怪的debug输出也没删干净,没想到遇到了好心的周国杰大佬,帮忙给de了一波bug(感动.jpg)才不至于死的太过于惨烈,让我感到了同学情!!特此表示感谢!这也提示我们,框架一旦确定好,一定要有十足的把握认为框架的确存在巨大漏洞再考虑推倒重写,不要在几种类似的方案中摇摆不定,这山望着那山高,其实是在浪费时间,很可能不慎获得一次意想不到的熬夜机会。

     

     

    第七次作业

    第七次作业是一次新的系列作业,继续实践线程安全设计,对交互关系的分析尤为重要。

    这是我在需求分析中的部分分析:

    (1)乘客交互的数据特征:

    乘客->调度器:请求位置、目标位置、系统获取请求发出的时间

    调度器->乘客:是否有车响应等反馈

    (2)乘客交互的时间特征:

    乘客->调度器:有乘客请求会被移交到调度器,调度器只要发现立即处理

    调度器->乘客:3秒时间一到派单,并及时给出反馈

    (3)出租车交互的数据特征:

    出租车->调度器:出租车的状态信息,包括车辆编号、当前状态、所在位置等,便于调度器判断出租车是否可用

    调度器->出租车:分配订单,告知出租车有新单,浮动信任值

    (4)出租车交互的时间特征:

    出租车->调度器:每200ms做出一次移动,并随即向共享对象更新一次状态信息

    调度器->出租车:抢单窗口关闭后进行派单,派单后立即告知相应出租车的共享对象有新单

    MakeMap:负责读入文件、创建地图、创建邻接矩阵、计算最短距离

    CarInformation:记录出租车信息和接单请求信息的共享对象

    Input_Handler:处理输入的线程,将合法输入放入到请求队列中去

    Request_List: 共享对象请求队列

    Taxi:出租车运营接客等

      因为出租车之间有较强的独立性和严格的时间特征所以设计为一个出租车对应一个线程,出租车群体具有并发特征,并发行为有:检测接单、接单状态(以最短路径行走)、服务状态(以最短路径行走)、停止状态、等待状态(随意走动)。

      因为调度器可以模拟出租车调度平台所以将调度器设计为一个线程,扫描请求队列依次按时加车、派单并负责给用户反馈。

      设计一个输入线程,用于即时处理发出的请求并添加到请求队列。

    另外hashmap是一个很好的key-value存储工具,强推。

     


    (2) 基于度量来分析自己的程序结构


    第五次作业

     

    缺点:1、if else ,switch case 等判断语句会增加圈复杂度,导致代码复杂,不方便维护。

    以后写代码按照制定的代码规范来,同时避免写 if,for 多层嵌套的代码,否则圈复杂度和块复杂度都会变得很高。

    2、有些方法的参数数目比较多,说明对数据的管理分配存在巨大的优化空间,必须减少God类的出现也要注意数据传递冗余。

    第六次作业

     

     

    缺点:这次作业不仅没有把目录完成的特别好,而且因为初次接触文件的功能性内容,对触发器的设计圈复杂度和块复杂度都很大,是因为算法太繁琐嵌套太多所致。

    第七次作业

     

    缺点:可以发现这次作业check方法的问题尤为突出,是因为在这个方法中,一次性对所有的读入进行阶段性解析判断合法性处理,if else 和switch非常多,下次作业一定要思考如何简化合法性判断。

    这几次作业的优点是代码量在努力减少,方法封装也用的越来越好,但进步空间还很大。


    (3)分析自己程序的bug


    第五次:1、程序不能自己停止,因为第一次使用多线程,线程之间的结束先后逻辑关系设计存在漏洞,导致当输入END时如果调度器大队列不为空或大队列不为空而小队列为空时个别线程在特定情形下无法结束。互测结束后已经对应程序进行了相应修改。

    2、split使用错误:一定要记住这个神坑的特例;;;;;;;;,应该使用split(“;”,-1),同时注意对空分割的判断。

    3、输出时间多了0.1s:多线程运行需要时间,会有一定的时间误差,可以选用假时间或sleep(本来标准的睡眠时长-程序运行至此消耗的时长)

    第六次:监控目录存在一系列BUG,原因是递归建立快照的写法存在BUG,对递归的学习必须加强。作业的功能不全,以后有时间要继续研究。

    第七次:吸取第五次作业的教训改用了假时间,没有被发现功能性BUG,但因为仓促写了文件读入,忘记判断创建地图的返回值,导致错误地图读入不能结束程序。


    (4)分析自己发现别人程序bug所采用的策略


      前两次作业的BUG树比较细致,基本功能的测试都是依照BUG树去构造的,特殊边角的测试主要针对代码的特点,有选择的去读一些自己认为易错的地方的代码,比如快照对比、快照建立、电梯调度器的设计逻辑等,最后一次作业主要是记住自己写程序的时候认为容易忽略的地方比如出租车系统出租车状态机中关于停止的设计、关于调度器抢单的设计逻辑、以及有意攻破线程安全的一些方案,有意制造矛盾点,使得线程交互设计的弱点暴露,同时对于时间的掐算也要有自己的一套判断标准。


    (5)心得体会


      经过三次多线程工程模式的训练,我从对JAVA多线程陌生到能根据JAVA代码的运行特点去修正线程安全问题实在经历了太多挑战的辛酸,但正是这种坚持一下就看到胜利的终点的状态让我学会了很多。线程安全方面我学会了简化共享对象、共享对象的安全封装等,可以从一个设计者的角度去分析需求,分析哪些应该被设计为线程,哪些应该附带共享对象才能交互。线程安全方面需要有合理的访问机制,包括关键字和wait notify的正确使用。设计方面应该有归一思想,能简则简,尽量减少线程竞争,不变动的数据不要使用共享对象而是使用拷贝的方法等。在线程竞争不可避免时,设计合理的访问方案,尽量减少结果的不确定性,是我们永恒的追求。

      另外,写代码最好还是多和同学讨论,只要不触及代码层面都是有益无害的,多人讨论会有更多启发,也容易及时纠正思想误区,避免很多思维漏洞导致的BUG。尽量不要先写一个特别大的框架然后一点点细节去重新改,因为可能省略的地方太多最回头写完想打补丁都忘了,毕竟细节决定成败。

  • 相关阅读:
    Corosync+Pacemaker+DRBD+Mysql高可用HA配置
    高可用集群corosync+pacemaker之crmsh使用
    nfs+DRBD+corosync+pacemaker 实现高可用(ha)的nfs集群
    nginx匹配
    2011年的最后一篇
    Python自然语言处理学习笔记(66):7.7 小结
    Python自然语言处理学习笔记(65):7.6 关系抽取
    Python自然语言处理学习笔记(64): 7.5 命名实体识别
    Python自然语言处理学习笔记(67):7.8 扩展阅读
    Python自然语言处理学习笔记(68):7.9 练习
  • 原文地址:https://www.cnblogs.com/One-Is-All/p/8976868.html
Copyright © 2011-2022 走看看