热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

BUAA–OO–第二单元作业总结

BUAA-OO-第二单元作业总结1.程序架构1.1第五次作业​第五次作业要求模拟一个多线程实时电梯系统,其中乘客请求仅包括在本座内的上下移动1.1.1概述主要思路是:读入线程读入

BUAA - OO - 第二单元作业总结

1. 程序架构




1.1 第五次作业

​ 第五次作业要求模拟一个多线程实时电梯系统,其中乘客请求仅包括在本座内的上下移动


1.1.1 概述

主要思路是:读入线程读入请求至等待队列 => 主控线程分配请求至各个调度队列 => 各个电梯线程处理调度队列中的请求



  • 创建请求类(其实可以用官方包),用以记录乘客状态并作为队列的储存单元



  • 基于生产者-消费者模式



    • 创建生产者:输入线程。读入输入信息并转化为调度请求,将其填入等待队列

    • 创建托盘类:作为生产者和消费者存取请求的媒介

    • 创建消费者:电梯线程。处理调度队列中的调度请求,即完成开关门、寻地与运送乘客的任务



  • 创建主控线程,将等待队列中的请求分配给电梯对应的调度队列



以此基本完成 读入 => 分配 => 处理 的模式。(实际上还需要单独封装一个线程安全的输出类,在本次作业中不慎忽略,后续会加以补充)

接下来我们结合UML类图对分配逻辑和处理方法进行一些介绍。


1.1.2 UML类图

HW5


调度设计

读入 => 分配



  • 首先用读入线程将请求放入waitQueue

  • 同时要为不同的电梯准备不同的processingQueue

  • 接下来主控线程waitQueueget()出请求,再put()进楼座对应的processingQueue

分配 => 处理



  • 为了防止多线程并行在对同一个队列进行存取,首先Tray中所有方法添加synchronized关键字以保障原子性



  • 接下来要为电梯和乘客设定不同的状态:等待,上行,下行



  • 然后是调度策略的设计,我们模仿现实中的电梯进行策略设计:



    • <启动>

      • 此时为起点

        • 未上电梯的请求状态为等待,且有各自方向

        • 电梯状态为等待



      • processingQueueget()最远的等待请求,并将其加入目标队列

      • <更新><移动>至请求起点,<交互>



    • <移动>

      • status方向移动,每一层都要输出ARRIVE信息

        • 若本层有同向等待乘客或到达乘客,<交互>

        • 继续<移动>



      • 当彻底清空电梯,且processingQueue中无新请求时,电梯重回等待状态



    • <交互>

      • 开门<清除><添加>关门



    • <添加>

      • 将所有本层同向等待乘客加入运行队列

      • 直到装满或加完后,<更新>

      • 更改所有乘客状态为运行方向



    • <清除>

      • 让所有到达目的地的乘客离开电梯,从运行队列中删除



    • <更新>

      • 更新目的地为指定值 或 距此最远请求的位置

      • 更新状态为到目的地的运行方向





线程结束



  • 当输入结束时,输入线程设置waitQueueisEndnotifyAll()并结束

  • 主控线程在操作waitQueue时探测到isEnd(),则为每个processingQueue设置isEnd()notifyAll()并结束

  • 每个电梯线程在操作processingQueue时探测到isEnd(),结束


1.1.3 基于度量的结构分析

对于代码规模,有如下statistics analysis

HW5scale

对于类,有如下metrics analysis

HW5class

对于方法,有如下主要部分metrics analysis

HW5Method

经过讨论和学习研究,终于勉强完成了第一次多线程程序写作。利用一次性接满同一方向乘客的策略完成了最基本的电梯调度。代码量尚为适中,主要类中仅消费者线程较为臃肿,总体来说尚可接受。




1.2 第六次作业

​ 第六次作业允许水平环形电梯的存在,并增设了添加电梯的请求,而所有乘客请求仍不涉及换乘


1.2.1 概述

主要思路仍是遵循 读入 => 分配 => 处理 模式设计,只是要区分两种请求和两种电梯



  • 自官方包派生乘客请求类,用以记录乘客状态并作为队列的储存单元

  • 自官方包封装输出类,完成输出的线程安全设计

  • 基于生产者-消费者模式

    • 更新生产者:输入线程。将乘客请求填入等待队列,为新建电梯配置调度队列

    • 更新托盘类:作为生产者和消费者存取请求的媒介,增设分配策略

    • 更新消费者:电梯线程。处理调度队列中的调度请求,即完成开关门、寻地与运送乘客的任务

      • 派生横向电梯线程

      • 派生纵向电梯线程





  • 更新主控线程,将等待队列中的请求分配给电梯对应的调度队列,增设分配策略

接下来我们结合UML类图对分配逻辑和处理方法进行一些介绍。


1.2.2 UML类图

HW6


调度设计*

读入 => 分配



  • 首先用读入线程将请求放入waitQueue



  • 同时要为不同的线路准备不同的ArrayList processingQueue,其中



    • 【1-10】代表十条水平线路

    • 【11-15】代表五条竖直线路

    • 而每条线路之下都为每个新电梯配置新的processingQueue



  • 接下来主控线程从waitQueueget()出请求,再put()进楼座对应的processingQueue中,

    其中请求分配的策略值得一提,这里我暂时没有想到合适的方法,只是讨论出了一个补救方法



    • 其实完全可以自由竞争(

    • get()时可选择路程最远(且离边缘最近)的请求

    • put()时可选择当前路径上调度队列中请求最少的电梯接手



分配 => 处理



  • 为了防止多线程并行在对同一个队列进行存取,首先Tray中所有方法添加synchronized关键字以保障原子性



  • 接下来要为电梯和乘客设定不同的状态:等待,前进,后退



  • 然后是调度策略的设计,我们模仿现实中的电梯进行策略设计:



    • <启动>



      • 此时为起点

        • 未上电梯的请求状态为等待,且有各自方向

        • 电梯状态为等待



      • processingQueueget()最远的等待请求,并将其加入目标队列

      • <更新><移动>至请求起点,<交互>



    • <移动>



      • status方向移动,每一处都要输出ARRIVE信息

        • 若本层有同向等待乘客或到达乘客,<交互>

        • 继续<移动>



      • 当彻底清空电梯,且processingQueue中无新请求时,电梯重回等待状态



    • <交互>



      • 开门<清除><添加>关门



    • <添加>



      • 将所有本层同向等待乘客加入运行队列

      • 直到装满或加完后,<更新>

      • 更改所有乘客状态为运行方向



    • <清除>



      • 让所有到达目的地的乘客离开电梯,从运行队列中删除



    • <更新>



      • 更新目的地为指定值 或 距此最远请求的位置



      • 更新状态为到目的地的运行方向



        • 横向电梯可在启动时选择如下策略确定最近方向(经课下讨论得出结论)

        (dst - src + 5) % 5 > (src - dst + 5) % 5 ? "INC" : "DEC";


        • 此后可直接固定运行方向(经研讨课上的同学提点,此方法效率甚至可能更高)







线程结束



  • 当输入结束时,输入线程设置waitQueueisEndnotifyAll()并结束

  • 主控线程在操作waitQueue时探测到isEnd(),则为每个processingQueue设置isEnd()notifyAll()并结束

  • 每个电梯线程在操作processingQueue时探测到isEnd(),结束


1.2.3 基于度量的结构分析

对于代码规模,有如下statistics analysis

HW6Scale

对于类,有如下metrics analysis

HW6Class

对于方法,有如下主要部分metrics analysis

HW6Method

由于较差的休息状况和OS的失利,我被迫在第六次作业上依赖了很多外部帮助,尽管效果并不很令人满意,不过最终还是完成了。在本次作业中,主控线程和托盘类过于臃肿,其中许多方法即便经过数次refactor也依然有相当高的复杂度,大抵是没有经过深入优化和提取共性方法所致。日后定当在各种方面引以为戒。




1.3 第七次作业

​ 第七次作业要求满足个性化电梯的定制需要(速度、容量、横向电梯的停留条件)和乘客的换乘需要


1.3.1 概述

主要思路仍是遵循 读入 => 分配 => 处理 模式设计,只是需要拆解换乘请求明确换乘方法厘清类间关系



  • 自官方包封装输出类,完成输出的线程安全设计



  • 为了换乘



    • 自官方包派生乘客请求类,用以记录乘客状态并作为队列的储存单元



    • 新建请求链类,根据通达情况将请求拆分为不需换乘的子请求链



      • 若是在分配完后添加了新的电梯,这样静态的拆分方法不一定足够优越,只是真的没时间做动态更新了



    • 新建信号量类使每个请求链作为一个单独的资源存在,以此



      • 避免多个线程插手同一任务的不同阶段

      • 避免线程由于针对请求队列的操作而提前结束或不能结束





  • 基于生产者-消费者模式



    • 更新生产者:输入线程。将乘客请求填入等待队列,为新建电梯配置调度队列

    • 更新托盘类:作为生产者和消费者存取请求的媒介,更新分配策略

    • 更新消费者:电梯线程。处理调度队列中的调度请求,即完成个性化设定、开关门、寻地与运送乘客的任务

      • 派生横向电梯线程,添加换乘位表

      • 派生纵向电梯线程





  • 更新主控线程,将等待队列中的请求分配给电梯对应的调度队列,更新分配策略



  • 增设电梯工厂类,考虑到电梯的设计愈发独立和完善,选择将建立和存储电梯的任务从输入和主控线程中解放出来



  • 增设等待队列类,考虑到所有线程都要和等待队列直接交互,选择让所有线程面对独立的单例模式等待队列类



接下来我们结合UML类图对分配逻辑和处理方法进行一些介绍。


1.3.2 UML类图

HW7


调度设计+

读入 => 分配



  • 首先用读入线程将请求转化为原子请求链,放入waitQueue



  • 同时要为不同的线路准备不同的ArrayList processingQueue,其中



    • 【1-10】代表十条水平线路

    • 【11-15】代表五条竖直线路

    • 而每条线路之下都为每个新电梯配置新的processingQueue



  • 接下来主控线程从waitQueueget()出请求,再put()进楼座对应的processingQueue中,

    其中请求分配的策略有所更新



    • get()时可选择具有最远路程(且离边缘最近)的请求的所在请求链



    • put()时可选择当前路径上调度队列中请求最少的电梯接手,但此电梯一定满足一次换乘条件

      ((switchInfo >> (src -'A')) & 1) + ((switchInfo >> (dst -'A')) & 1) == 2




分配 => 处理



  • 此处对请求链操作时,只操作其目前需要做的一项子请求



  • 为了防止多线程并行在对同一个队列进行存取,首先Tray中所有方法添加synchronized关键字以保障原子性



  • 信号量本身的PV操作都要保障原子性,所以毫无疑问地需要添加synchronized关键字



  • 接下来要为电梯和乘客设定不同的状态:等待,前进,后退



  • 然后是调度策略的设计,我们模仿现实中的电梯进行策略设计:



    • <启动>



      • 此时为起点

        • 未上电梯的请求状态为等待,且有各自方向

        • 电梯状态为等待



      • processingQueueget()最远的等待请求,并将其加入目标队列

      • <更新><移动>至请求起点,<交互>



    • <移动>



      • status方向移动,每一处都要输出ARRIVE信息

        • 若本层有同向等待乘客或到达乘客,<交互>

        • 继续<移动>



      • 当彻底清空电梯,且processingQueue中无新请求时,电梯重回等待状态



    • <交互>



      • 开门<清除><添加>关门



    • <添加>



      • 将所有本层同向等待乘客加入运行队列

      • 直到装满或加完后,<更新>

      • 更改所有乘客状态为运行方向



    • <清除>



      • 让所有到达目的地的乘客离开电梯,从运行队列中将其删除

      • 对每个离场的乘客,在其请求链中清除本次的请求,更新请求链

        • 若尚未完成,则把更新后的请求重新交给waitQueue,即准备换乘调度

        • 否则,使信号量释放资源,表示该请求已经完成





    • <更新>



      • 更新目的地为指定值 或 距此最远请求的位置



      • 更新状态为到目的地的运行方向



        • 横向电梯可在启动时选择如下策略确定最近方向(经课下讨论得出结论)

        (dst - src + 5) % 5 > (src - dst + 5) % 5 ? "INC" : "DEC";


        • 此后可直接固定运行方向(经研讨课上的同学提点,此方法效率甚至可能更高)







线程结束



  • 当输入结束时,输入线程用信号量依次检验所有请求链的完成,未完成则不结束

  • 输入线程结束时,设置waitQueueisEndnotifyAll()并结束

  • 主控线程在操作waitQueue时探测到isEnd(),则为每个processingQueue设置isEnd()notifyAll()并结束

  • 每个电梯线程在操作processingQueue时探测到isEnd(),结束


1.3.3 基于度量的结构分析

对于代码规模,有如下statistics analysis

image-20220503145918180

对于类,有如下metrics analysis

image-20220503150031863

对于方法,有如下主要部分metrics analysis

image-20220503150127003

直到现在还有RTLE存在,只能说是一开始就没有设计好自己的架构导致的


1.3.4 时序图

拼接起来太过复杂,且毫无启发意义,故针对几个主要类分别建立时序图



  • InputHandler

    image-20220504140333450


  • MainClass



image-20220504141703628



  • ElevatorFactory

    image-20220504140654215


  • Horizontal为例

    QQ截图20220504141924





2. Bug修复

自己的代码都来不及写,拿大家的互测代码学习一下也就罢了,真没精力找bug


2.1 第五次作业



  • 自己的bug



    • Bug1:输出时间戳未递增



      • 原因:没有封装线程安全输出类

      • 评价:未能理解什么是线程安全,忽视了多线程的特点



    • Bug2:输出错误



      • 原因:没有在每一层都输出ARRIVE

      • 评价:没有仔细看指导书,该打



    • Bug3:RTLE



      • 原因:调度策略编写有误

      • 评价:需更多地进行评测和分析讨论






2.2 第七次作业



  • 自己的bug



    • Bug1:RTLE



      • 原因:...线程结束标记和信号量检验写反了

      • 评价:...治治眼睛



    • Bug2:真·RTLE



      • 原因:调度策略编写并未得到足够优化

      • 评价:动态规划路径可能会更好一些,但也许难度更大








3. 心得体会

最难单元



  • 一定要读清楚指导书,一定要读清楚指导书,一定要读清楚指导书

  • 提前了解未来可能存在的需求并在架构设计中加以考虑,会大大降低重构的成本

  • 认真了解各种设计模式,如单例模式、工厂模式,又如生产者消费者模式、观察者模式等等

  • 死锁:对共享变量合理上锁,嵌套锁保证顺序无误

  • 轮询:对wait()条件要合理判断【该改变线程结束条件的时候,不要wait(),不然RTLE】



推荐阅读
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • Week04面向对象设计与继承学习总结及作业要求
    本文总结了Week04面向对象设计与继承的重要知识点,包括对象、类、封装性、静态属性、静态方法、重载、继承和多态等。同时,还介绍了私有构造函数在类外部无法被调用、static不能访问非静态属性以及该类实例可以共享类里的static属性等内容。此外,还提到了作业要求,包括讲述一个在网上商城购物或在班级博客进行学习的故事,并使用Markdown的加粗标记和语句块标记标注关键名词和动词。最后,还提到了参考资料中关于UML类图如何绘制的范例。 ... [详细]
  • 本文讨论了微软的STL容器类是否线程安全。根据MSDN的回答,STL容器类包括vector、deque、list、queue、stack、priority_queue、valarray、map、hash_map、multimap、hash_multimap、set、hash_set、multiset、hash_multiset、basic_string和bitset。对于单个对象来说,多个线程同时读取是安全的。但如果一个线程正在写入一个对象,那么所有的读写操作都需要进行同步。 ... [详细]
  • 第七课主要内容:多进程多线程FIFO,LIFO,优先队列线程局部变量进程与线程的选择线程池异步IO概念及twisted案例股票数据抓取 ... [详细]
  • Unit4博客&课程总结Unit4作业的架构设计本单元作业的设计我分为了三个模块处理:模型构建+预处理+任务函数,前两部分即为整个图的完整构建,第三部分即为实现题目要求的查询方法。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • C++ STL复习(13)容器适配器
    STL提供了3种容器适配器,分别为stack栈适配器、queue队列适配器以及priority_queue优先权队列适配器。不同场景下,由于不同的序列式 ... [详细]
  • java线程池的实现原理源码分析
    这篇文章主要介绍“java线程池的实现原理源码分析”,在日常操作中,相信很多人在java线程池的实现原理源码分析问题上存在疑惑,小编查阅了各式资 ... [详细]
  • 第四单元和课程总结:简单的架构设计意识
    一、第四单元架构设计总结第一次作业由于需要按名查找类图模型,于是建立&amp;quot;Class&amp;quot;类进行管理由于方法具有参数导致类中存在二级结构 ... [详细]
author-avatar
渊博的大盗zhang_618
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有