第一组-摆烂小组-第三次团队作业:项目系统设计与数据库设计 这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzzcxy/ZhichengSoftengineeringPracticeFclass?filter=all 这个作业要求在哪里 https://edu.cnblogs.com/campus/fzzcxy/ZhichengSoftengineeringPracticeFclass/homework/12601 这个作业的目标 完成系统设计说明书与数据库设计说明书 小组的组号和队名 第一组摆烂小组 小组的队长姓名 吴泽伟 一、预计开发计划时间安排 周数 任务安排 当前进度 第十周 1、制作系统设计书2、制作数据库说明书3、答辩ppt 已完成 第十一周 1、搭建vue框架,实现基本页面跳转2、后端连接数据库写出基本逻辑 正在1/3 第十二周 1、前端实现所有界面设计2、后端完成代码书写给出接口 未完成 第十三周 初步前后端整合 未完成 第十四周 整合发现问题修改 未完成 第十五周 调试项目,修改一些bug 未完成 第十六周 核查总结 未完成 二、预期开发计划分工安排 组员 角色 负责的开发部分 吴泽伟 前端 前端全部界面设计,负责与后端数据对接 黄鸿斌 后端 后端功能实现,对后端进行接口调试、书写接口文档 林镕卫 后端 辅助黄鸿斌完成任务 肖灿明 后端 解决数据库的方面 庄文志 前端 辅助吴泽伟设计前端 骆颖飚 测试 测试软件,寻找不足,对软件提出建议 三、设计图 1、体系结构图 2、功能模块图 3、类图 4、E-R图 5、数据库设计图 6、系统安全 1、数据库作为管理系统的基础,保存着重要的用户信息和文件信息,故数据的完整性和安全性尤为重要。
文件和文件系统 文件是Linux系统中最基础最重要的抽象。Linux遵循一切接文的理念,所以大多数的交互操作都是通过读写文件来完成。文件必须先打开才能访问,文件打开方式有:只读,只写和读写三种方式 文件打开后是通过文件描述符来引用的,所谓文件描述符是从打开文件关联的元数据到文件本身的映射,在Linux内核中,文件用一个int类型的整数来表示,称为文件描述符。 文件描述符在用户空间共享,用户程序通过文件描述符直接访问文件。 文件类型有七种,其中块设备文件类型、字符设备文件类型、命名管道、UNIC域套接字为特殊文件(以文件来表示的内核对象) 特殊文件的存在是为了使某些抽象可以适用于文件系统,贯彻一切皆文件的理念,Linux提供系统调用来创建特殊文件。 文件系统:文件系统是用于有效的层次结构组织的文件和目录的集合。在文件和目录的全局namespace中,可以风别添加(挂载)和删除(卸载)文件系统。一般来说文件系统都是存在物理介质(磁盘)上的,但Liunx还支持只保存在内存上虚拟文件系统,以及存在于网络中的其他机器上的网络文件系统。物理文件系统保存在快设备文件中,如CD、硬盘、软盘、闪存。 挂载点:每个文件系统都需要挂载到namespace的特定位置。挂载点可以访问文件系统的根目录。 根文件系统:第一个被挂载的文件系统实在namespace的根目录“/”下。Linux必定有一个根文件系统,而其他文件系统的挂载点则是可以选的。 文件类型包括(7种): 普通文件类[-]:Linux中最常见的一种文件类型,包括纯文本文件;二进制文件(ASCII);数据格式的文件(date)以及各种压缩文件。 在Linux中,可以从文件的任意字节开始读写。对文件的操作是从某一个字节开始的,即文件“地址”。该地址称之为文件位置或者文件偏移。 文件偏移默认为0,按字节读写文件偏移也随之增加,文件偏移也可以手动设置给定值,可以超出文件结尾,超出部分会默认填充0。不允许在文件起始位置写入,在文件中间写入会覆盖原有数据,并不会导致原有数据向后偏移。 文件大小通过字节数来计算,称为“文件长度”,文件长度可以通过截断操作进行改变,截断后大小可以大于原文件,超出部分填充0。 文件描述符,系统会为每个打开的文件实例提供唯一文件描述符,进程可以共享文件描述符,支持多进程使用同一个文件描述符。 文件虽然是通过文件名进行访的,但文件本身其实并没有直接和文件名进行关联,与文件本身关联的是索引节点inode(是文件系统为该文件分配的唯一整数值,但是在整个系统中不一定唯一),索引节点会保存和文件相关的元数据,如文件修改时间戳、所有者、类型、长度、文件数据的位置,但不包含文件名。 索引节点、文件描述符 inode 或i节点是指对文件的索引。如一个系统,所有文件是放在磁盘或flash上,就要编个目录来说明每个文件在什么地方,有什么属性,及大小等。就像书本的目 录一样,便于查找和管理。这目录是操作系统需要的,用来找文件或叫管理文件。许多操作系统都用到这个概念,如linux, 某些嵌入式文件系统等。当然,对某个系统来说,有许多i节点。所以对i节点本身也是要进行管理的。 在linux中,内核通过inode来找到每个文件,但一个文件可以被许多用户同时打开或一个用户同时打开多次。这就有一个问题,如何管理文件的当前位移 量,因为可能每个用户打开文件后进行的操作都不一样,这样文件位移量也不同,当然还有其他的一些问题。所以linux又搞了一个文件描述符(file descriptor)这个东西,来分别为每一个用户服务。每个用户每次打开一个文件,就产生一个文件描述符,多次打开就产生多个文件描述符,一一对应, 不管是同一个用户,还是多个用户。该文件描述符就记录了当前打开的文件的偏移量等数据。所以一个i节点可以有0个或多个文件描述符。多个文件描述符可以对 应一个i节点。
目录文件类型[d]:就是目录,可使用# cd命令进入 通过文件节点访问文件很繁琐且潜在安全漏洞,因此访问文件通常是通过文件名从用户空间打开,而目录则提供访问文件需要的名称。 目录是可读名称到索引编号之间的映射,而名称和索引节点之间的配对称之为链接。 映射在物理磁盘上的形式是通过文件系统的内部代码来实现和管理的 目录和普通文件的区别: 从概念上可以把目录看做普通文件,其区别主要在于目录包含文件名称到索引节点的映射,内核通过该映射将文件名解析为索引节点。 用户打开文件: 用户空间的应用请求打开指定文件 ----> 内核打开包含改文件的目录搜索该文件 -----> 内核根据文件名获取索引节点编号 ------> 通过索引节点编号找到该节点 块设备文件类型[b]:存储数据以供系统存取的接口设备,简单来说就是硬盘。例:一号硬盘的代码/dev/hda1 块设备是作为字节数组来进行访问。设备驱动把字节映射到可寻址的设备上,用户空间可以按任意顺序随意访问数组中的任何字节。 块设备最小寻址单元:扇区(sector),扇区是设备的物理属性。扇区大小一般是2的指数倍,通常为512字节 文件系统最小寻址单元:块(block),块是文件系统的抽象,块大小一般是2的指数倍乘以扇区大小,通常比扇区大但必须小于页 内存最小寻址单元:页(page),页是内存的最小寻址单元 字符设备文件类型[c]:串行端口的接口设备,例:键盘、鼠标等 字符设备是作为线性字节对列来访问。设备驱动程序把字节按顺序写入对列,用户空间程序按照写入对列的顺序读取数据。 套接字文件类型[s]:这类文件通常用在网络数据连接。可以启动一个程序来监听客户端的要求,客户端就可以通过套接字来进行数据通信。最常在 /var/run目录中看到这种文件类型 socket是进程间通信的高级形式,支持不同进程间的通信,这两个进程可以在同一台机器上也可以在不同的机器上。 管道文件类型[p]:FIFOf(irst-in-first-out 先进先出)是用文件描述符来作为进程间通信的机制,它主要的目的是,解决多个程序同时存取一个文件所造成的错误。进程间通信,创建无名管道或者有名管道时会用 普通管道是将一个程序的输出以“管道”的形式作为另外一个程序的输入 命名管道和普通管道一样,但它是通过FIFO特殊文件来访问的。 链接文件类型[l]:链接文件是Linux文件系统的一个特性。如需要在系统上维护同一文件的两份或多份副本,除了保存多份单独的物理文件副本之外,还可以采用保存一份物理文件副本和多个虚拟副本的方法。链接是目录中指向文件真实位置的占位符。 在Linux中有两种不同类型的文件链接: 1.
Postman 删除工作空间的地方比较隐蔽。
在你打开工作空间后,选择工作空间的设置。
在后续的页面中,将会提示你是否选择删除这个工作空间。
在随后的页面中,提示你输入工作空间的名字,然后单击确定删除即可。
https://www.ossez.com/t/postman/13936
2022_OO第二单元总结 一、架构分析 1.homework5 简单的生产者消费者模型 UML类图: hw5没有做过多的要求,五个座的电梯都是独立运行,比较简单。一开始我也是按照输入->调度器->电梯进行的架构,但是后来发现调度器实际上就是根据指令的座号分配到相应的队列中,没有起到调度的作用,而且浪费了一个线程,所以后来做了优化,把调度器并入了Input线程中,接收到输入信息后直接分发到对应的座(电梯)中。另外,我没有使用官方包里的构造方法,而是自己重写了Person类,又新增了PersonQueue类(借鉴了上机的架构),方便处理Person队列中的插入与删除的线程安全,每个电梯线程和Input线程都由一个PersonQueue对象作为托盘。
这样做相当于每座只用了一个托盘,就完成了输入到电梯执行的信息传递,相对之前的架构更高效;但是这也导致了Input线程不仅要处理输入,还要兼顾分发,为hw7的复杂结构埋下了隐患(hw7迫不得已又增加了调度器)。
电梯算法方面,三次作业我都使用了look算法,尽量不转向以提高效率。同学们很多都是用的这个算法,hw6和hw7的架构分析中不再赘述。
2.homework6 环形电梯和多电梯 UML类图: hw6中增加了横向环形电梯和每层/每座多电梯同时运行的要求,没有换乘要求(课程组的仁慈)。大的结构方面,我没有进行大的改动,线程还是只有Input和Elevator两类,另外添加了SafeOutPut类,方便安全输出,不用逐个加锁。
对于横向电梯,我沿用了纵向电梯的的look算法。为了方便捎带,我在Person类中追加了两个状态 isUp 和 isRight,实例化Person对象时就保存了这个人向上/下、向左/右的请求,进行捎带时直接读取人的方向状态即可,省去了实时判断的步骤。
对于同座/同层多电梯,我进行了调度器和自由竞争两方面的尝试。调度器的优点是分配透明,每个人分配到的电梯是可查询的,但缺点是写出一个效率高的动态调度器是十分麻烦的,要持续读取每个电梯的运行状态,首先电梯状态共享就是一个问题,容易出现线程安全问题,其次调度算法也无法保证最优;自由竞争的优点是方便好写,只要每座/每层的所有电梯共享同一个队列(PersonQueue),做好共享队列的线程安全即可,而且根据大量数据的检验,自由竞争的效率也很好,但缺点是有时候电梯会“陪跑”,人数少的时候浪费资源,一个人能进哪个电梯也是随缘的。
经过深思熟虑(水平不行而且懒),我还是选择了自由竞争的策略,但写的过程中也发现,自由竞争并不是那么省事。比如刚写成的时候出了这么一个bug,如果多个电梯同时到达一层,即使这层只有一个人,每个电梯还是都会开门,浪费了停下的时间。这个bug我通过给人记录一个属性:要上的电梯ID 解决了。另外就是大家基本上都做过的优化:量子电梯,也就是停满0.4秒后可以闪现到相邻层/座中,这个我通过调用并记录系统时间解决了。
至于动态新增电梯,算是比较简单的一部分,只要把新增的电梯共享到相应的队列中即可,不再赘述。
3.homework7 换乘和电梯定制 UML类图: hw7增加了电梯定制和换乘的要求,算是一个电梯系统的“完全体”,但只有横向电梯能定制停止座号,纵向电梯还是每层都能停,也算是课程组放了一些水(感谢)。
大的结构方面,由于一开始的结构中Input功能过多,导致扩展能力不足,没法进行乘客路径的重分配,所以还是花了比较多的时间,增加了Distributor类,专门用于建立路径和分配到相应队列,也解放了Input,这次架构中的Input只需要初始化队列,处理新建乘客和新建电梯即可。另外把原来的Person类改成了PersonReq,新增的Person类只有一个ArrayList,按顺序保存PersonReq,也就是路径。
电梯定制算是这次作业的开胃菜,只要把速度、容量和可停靠信息在新建电梯的时候输入进去即可,无需过多赘述。
重头戏自然是换乘的调度策略,我的策略也是一步步复杂起来,最后停在了大概中等复杂的水平(还是太菜了)。一开始完全是纯静态分配,新建乘客对象的时候就确定了整个路径,不能充分利用后续加入的电梯,并且电梯的选择也有一定的问题,容易很多人堆在一个电梯里;后来改进了算法,每新增一个横向电梯,就把所有等待中乘客的路径重新规划一次,电梯的选择也倾向于平均分配,下面是详细解释:
首先判断是否需要横向移动,如果不需要,则直接一段纵向路径即可;如果需要横向移动,则如下图: 首先遍历所有的横向电梯,如果此电梯在起点座和终点座都能开门,则进行下一步判断:
(1)如果该电梯楼层等于出发层或终点层,则将该层设为中转层;
(2)如果该电梯楼层不等于出发层或终点层,如果|电梯层 - 出发层| + |电梯层 - 终点层| < minNum,则将该层设为中转层,并更新minNum;
由于遍历一次后,选中的总是队列中最后一个满足条件的电梯,所以遍历完成后把该电梯调到队列首,达到平均分配的效果;
这个分配策略胜在比较简单,但是没有考虑每层可用电梯数和电梯繁忙程度,而且所有横向路径都只有一段,不会出现A->B, B->C等情况,不能保证最短路径。
二、同步块设计 三次作业同步块设置相似,取最后一次详细介绍:
Input类中的同步块 synchronized (inputQueues) { //新建每层、每座的自由竞争队列 for (i = 0; i < 5; i++) { PersonQueue input2ele = new PersonQueue(id1[i]);//座自由竞争队列 inputQueues.
最近有个需求就是根据调用的函数名来获得函数所在的文件和该文件的路径,然后百度了好久终于找到了,记录下:
import inspect print(inspect.getfile(func)) 原文地址:http://ask.sov5.cn/q/hnCxdE3BK4
【基于Dubbo3.X版本】SpringBoot与dubbo整合的三种方式:
1)导入dubbo-starter,在application.properties配置属性,使用@DubboService【暴露服务】使用@DubboReference【引用服务】,使用@EnableDubbo注解。
2)保留dubbo的xml配置文件,导入dubbo-starter,使用@ImportResource导入dubbo的配置文件即可。不再使用@DubboService【暴露服务】使用@DubboReference【引用服务】,不使用@EnableDubbo注解。
3)使用注解API的方式,将每一个组件手动创建到容器中,让dubbo来扫描其他的组件,使用@DubboService【暴露服务】使用@DubboReference【引用服务】,使用@EnableDubbo注解。
【1】第一种方式-无dubbo配置文件 使用注解和application.properties,无dubbo额外配置文件。
provider
server.port=8088 dubbo.application.name=user-service-provider dubbo.registry.address=127.0.0.1:2181 dubbo.registry.protocol=zookeeper dubbo.protocol.name=dubbo dubbo.protocol.port=20881 dubbo.monitor.address=registry @DubboService暴露服务
//可在注解内设置属性如下 @Target({ElementType.TYPE, ElementType.METHOD}) @Inherited public @interface DubboService { /** * Interface class, default value is void.class */ Class<?> interfaceClass() default void.class; /** * Interface class name, default value is empty string */ String interfaceName() default ; /** * Service version, default value is empty string */ String version() default ; /** * Service group, default value is empty string */ String group() default ; /** * Service path, default value is empty string */ String path() default ; /** * Whether to export service, default value is true */ boolean export() default true; /** * Service token, default value is empty string */ String token() default ; /** * Whether the service is deprecated, default value is false */ boolean deprecated() default false; /** * Whether the service is dynamic, default value is true */ boolean dynamic() default true; /** * Access log for the service, default value is empty string */ String accesslog() default ; /** * Maximum concurrent executes for the service, default value is -1 - no limits */ int executes() default -1; /** * Whether to register the service to register center, default value is true */ boolean register() default true; /** * Service weight value, default value is -1 */ int weight() default -1; /** * Service doc, default value is empty string */ String document() default ; /** * Delay time for service registration, default value is -1 */ int delay() default -1; /** * @see DubboService#stub() * @deprecated */ String local() default ; /** * Service stub name, use interface name + Local if not set */ String stub() default ; /** * Cluster strategy, legal values include: failover, failfast, failsafe, failback, forking * you can use {@link org.
因为没启动,需要手动重启
systemctl start docker 然后docker ps 是没有任何容器启动的
所以要查询所有容器的id
docker container ls -a 这时候你能看到所有你之前被关掉的容器
start一下,就可以了
docker container start 01738895328c(container Id) stop同上了。
MySQL数据库远程连接开启方法 第一中方法:比较详细以下的文章主要介绍的是MySQL 数据库开启远程连接的时机操作流程,其实开启MySQL 数据库远程连接的实际操作步骤并不难,知识方法对错而已,今天我们要向大家描述的是MySQL 数据库开启远程连接的时机操作流程。1、d:\MySQL\bin\>MySQL -h localhost -u root这样应该可以进入MySQL服务器
复制代码代码如下: MySQL>update user set host = '%' where user = 'root';MySQL>select host, user from user; 2、MySQL>GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'mypassword' WITH GRANT OPTION予任何主机访问数据的权限3、MySQL>FLUSH PRIVILEGES修改生效4、MySQL>EXIT退出MySQL服务器这样就可以在其它任何的主机上以root身份登录啦!第二种方法:1、在控制台执行 mysql -u root -p mysql,系统提示输入数据库root用户的密码,输入完成后即进入mysql控制台,这个命令的第一个mysql是执行命令,第二个mysql是系统数据名称,不一样的。2、在mysql控制台执行 GRANT ALL PRIVILEGES ON *.* TO ‘root'@'%' IDENTIFIED BY ‘MyPassword' WITH GRANT OPTION;3、在mysql控制台执行命令中的 ‘root'@'%' 可以这样理解: root是用户名,%是主机名或IP地址,这里的%代表任意主机或IP地址,你也可替换成任意其它用户名或指定唯一的IP地址;'MyPassword'是给授权用户指定的登录数据库的密码;另外需要说明一点的是我这里的都是授权所有权限,可以指定部分权限,GRANT具体操作详情见:http://dev.
剩余参数配合解构赋值
剩余参数:将剩余的元素放到数组中
剩余参数以数组的形式存值
扩展运算符:
合并数组: 合并新数组,追加元素。。
伪数组转真数组
真数组可以使用push添加元素
Array扩展方法:
伪数组转数组 Array.from
返回一个新数组,arrarLike[0] = '张三'
输出:
Array.find()
返回符合条件的数组元素,找不到返回undefined
Array.findIndex()
1.传两个参数,数组和索引
2. 找不到就返回-1
Array.inclides()
返回布尔值
String 扩展方法
模板字符串:
反单引号、${ 方法、属性}引用
1.
2.
第二单元作业总结--目标选层电梯调度
目录第二单元作业总结--目标选层电梯调度一、架构设计及UML1、第一次作业2、第二次作业3、第三次作业4、UML协作图5、扩展可能二、线程安全控制1、设计模式2、线程安全类三、调度器设计1、纵向电梯调度2、横向电梯调度3、请求拆分方式4、程序结束方式5、量子电梯四、bug分析1、第一次作业的零散bug2、第二次作业的量子电梯bug3、第三次作业的线程bug五、测试及hack1、测试样例构造2、评测机编写六、心得体会
一、架构设计及UML 一个直观的总体架构图
1、第一次作业 完成第一次时,为了满足后续作业的迭代要求,我花了很多时间在架构设计上。虽然第一次作业对于线程安全的要求不是很高,我还是了解了一些多线程设计模式,并最终选择了生产者消费者模式。在本架构中,共有两个线程类,一个线程安全类。输入线程InputThread相当于是生产者,电梯线程Elevator相当于是消费者,线程安全类等待队列PersonQueue相当于是托盘。每一个楼座维护一个PersonQueue
Strategy策略类相当于是电梯的调度器,它通过访问这个楼座的PersonQueue以及这个电梯内部的乘客队列,来决定电梯下一步的目标。
电梯自身的状态不由Strategy控制,而是电梯内部维护一个有限状态机,通过电梯的内部类Status表示电梯目前所处的状态和电梯的状态转移,负责发出电梯的到达、开门等状态信息。电梯的状态可以抽象成三个状态:开门中Opening,运行中Running,决策中Waiting,这三个状态之间的状态转移是保证电梯正确运行的基础,也是我在第一次作业中花费最多时间设计和调试的部分。
2、第二次作业 第二次作业在架构上与第一次作业没有太大变动。第二次作业增加了多部电梯和环状电梯。
在我的架构中,要想解决同一个楼层或楼座多部电梯问题,只需要做好线程安全保证,让同一个楼座(楼层)的电梯能够正确的访问读写这个楼座(楼层)的乘客信息即可。在我的架构中,由于线程安全只需要在PersonQueue中保证,而这一点已经在第一次作业中处理完毕,因此自由竞争的调度策略完全不需要更改架构,只需要将这个队列加入更多可以访问它的电梯即可,并且自由竞争的调度策略在性能上有着不错的表现,因此综合考虑代码量和性能,我选择了自由竞争这种不需要很多代码即可实现不错性能的调度方式。
对于环状电梯,由于第一次作业中,我把策略单独写了一个类,并作为了电梯的参数,因此,这次迭代只需要增加一个环形调度策略,以及水平方向的等待队列即可,不需要对电梯的运行逻辑做任何的更改。为了更好的统一竖直运行和水平运行,我将楼座和楼层统一打包成位置类Position,这样横向调度和纵向调度的接口便可统一传入Positon,并且统一返回Position作为目的地,这样在电梯调用策略方法时,就不需要考虑策略的具体是水平还是竖直了。
3、第三次作业 第三次作业相比第二次作业,我在架构上依然没有太多的变化。第三次作业相比第二次作业,需要满足乘客的换乘需求,以及需要满足横向电梯的可开门位置定制。为了满足以上两个需求,相比第二次作业,我需要改进的三个点是:
支持环形电梯开门位置定制
规划乘客换乘路线
程序的结束逻辑需要更改,不再是输入结束,而是所有的请求都被处理完毕。
对于第一点 ,我在电梯类中增加了可达位置reachablePosition表,只有当前位置在电梯可达表中时,电梯才能开门,并且修改了调度策略,调度策略只会规划这个电梯可以接到的乘客(起点终点皆可达),并只会提供可以到达的目的地。
对于第二点,一个乘客请求可以被拆分成多个原子请求(不用换乘就能完成的请求),我实现了一个RequestList类,表示完成这个请求所需要完成的请求列表,并可以通过nowRequest方法得到该名乘客当前执行到的原子请求。当这个原子请求已经完成时,调用goToNext(),进入请求列表中下一个原子请求的处理。并将之前架构中所有用到PersonRequest的位置替换成RequestList.nowReques()即可。对于请求的拆分方法,在权衡了代码量和优化效果之后,我觉得标准策略其实是实现起来很简便并且效果不错的一种策略。标准策略时换乘次数最少的策略,虽然一定存在换乘更多次优于标准策略的情况,但我认为达到精准的判断换乘代价,并找到综合各种因素之后的最短路,所需要的工作量有点大。并且在大多数情况下,少换乘的性能是不错的,因此我还是选择了实现简便的标准策略。在标准策略的基础上,为了防止在有多个满足条件的中转层时,乘客堆积在某一中转层的情况发生,在这种情况下会随机安排一个中转层给乘客。最终的强测结果性能分也还不错,竟然还是三次作业中最高的一次,性价比很高。
对于第三点,我参考了实验代码,加入了RequestChecker类,从而实现请求的release和acquire,当acquire的次数和请求数量相等时,即可发出结束信号,通知所有线程结束,并最终结束程序。
4、UML协作图 5、扩展可能 由于将Strategy独立于电梯作为一个接口,因此若之后可以有更多的策略适应不同的情况,并且可以随时切换,不需要设计种类繁多的电梯 由于将楼座和楼层整合成位置类,贯通横竖向,而电梯的运行仅取决于电梯的策略,如果将电梯的策略更换成横向,那么一个原本纵向的电梯立即可以成为横向电梯。 二、线程安全控制 在三次作业中,我采用的线程安全控制模式差别不大,均是采用了生产者-消费者模式,并维护一个线程安全类来保证线程安全。
1、设计模式 在线程安全方面,我采用了生产者消费者的设计模式,输入线程和调度器相当于生产者,电梯相当于消费者。乘客请求队列相当于托盘。这样设计的好处是,我们只需要在托盘,也就是乘客请求队列类中保证线程安全即可,让线程安全操作全都集中在这一个类中。输入线程和电梯线程在设计时,只需关注其自身的逻辑行为,不需要再关心线程安全问题,大大降低了设计难度。
2、线程安全类 在我的架构中,线程安全仅集中在两个类中保证,分别是PersonQueue以及RequestChecker。
PersonQueue的任务是
保证在没有该电梯可以处理的请求的时候,确保电梯进入wait状态,暂时不让其从请求队列中获取请求
在输入线程向请求队列中增加请求之后,notifyall唤醒该队列对应的所有电梯线程
保证一个请求只能被一个电梯获取
RequestChecker的任务是
支持任务的释放,唤醒等待查收任务的线程 支持任务的查收,在没有可以查收的任务时,让线程等待 关于锁的选择,因为我的线程安全操作全都集中在一个类中,因此直接使用synchronized func(){}的形式,比较简便不易出错
三、调度器设计 1、纵向电梯调度 纵向电梯的调度设计主要集中在第五次作业中
纵向电梯调度中,我采用了look策略,即尽可能减少换向操作,捎带时仅捎带同方向的请求,在同方向上没有新的请求,并且电梯内部为空时,才能变换方向。
2、横向电梯调度 横向环形电梯的调度设计主要集中在第六次作业中
横向电梯调度中,我采用类似look的策略,在电梯内有乘客时,找出顺时针和逆时针中,满足所有电梯内乘客请求所需总路程最小的一个方向运行。在电梯中没有乘客时,按照当前运行方向选择一个最近的请求作为目的地。
3、请求拆分方式 对于请求的拆分,我使用了优化版的标准策略。对于如下情况
一 介绍 一款基于 vue.js 的轻量级的视频播放器插件插件
个性化配置 i18n 服务端渲染 画中画模式 事件订阅 易于开发 移动端适配 1.1 官方文档 https://core-player.github.io/vue-core-video-player/zh/get-started.html 1.2 安装和快速使用 第一步:安装 NPM
npm install --save vue-core-video-player 或者使用 yarn
yarn add -S vue-core-video-player 第二步:main.js引入 默认英语,你如果想成中文就加一个lang en: 英语 zh-CN: 简体中文 jp: 日本
import VueCoreVideoPlayer from 'vue-core-video-player' Vue.use(VueCoreVideoPlayer) //或者 Vue.use(VueCoreVideoPlayer, { lang: 'zh-CN' }) Use custom language data
import VueCoreVideoPlayer from 'vue-core-video-player' const kr = { ... dashboard : { btn: { .