BUAA_OO_博客作业二
1.作业设计策略
1.1第一次作业
第一次作业指导书要求是一个单部多线程傻瓜调度(FAFS)电梯的模拟,由于为了可扩展性和模块化设计,第一次作业我采用了三线程,即输入处理线程,调度器线程,电梯线程这三个线程进行通信工作,当然,实际上调度器线程仅仅做了把输入的请求进行转交给电梯线程这一个工作。输入处理线程和调度器线程共享reqlist
这个公共类进行消息传递,通过两个互斥的操作add
和fetch
进行队列任务的增加和提取,然后由调度器线程把提取到的任务交给电梯线程的worklist
,电梯拿到任务遍将任务从worklist
放到runlist
中,然后电梯进行模拟运行。我第一次作业的设计中有一个很糟糕的问题,就是调度器采取了暴力轮询的方式。
1.2第二次作业
第二次作业指导书要求是单部多线程可捎带调度(ALS)电梯的模拟,由于这次的任务其实我在第一次作业考虑到了,所以线程还是三线程,输入处理线程,调度器线程,电梯线程,也就是说我的基本架构没有变化,但是在调度器和输入处理线程的通信增加了wait
和notifyall
的线程维护操作,保证了没有暴力轮询的问题,当然这两个线程的通信还是依赖于reqlist
这个公共类。
这次作业在设计的时候我思考了一个架构问题,那就是我是让调度器进行捎带任务的处理还是让电梯本身进行任务的重新排序,在一番纠结后,我采取了伪智能化电梯的构建,因为我一开始的电梯框架包括了两个任务队列,一个是待运行队列worklist
,另一个是电梯当前运行负载队列runlist
,所以我可以依靠这两个队列进行任务更新工作,而且我的电梯是一个状态机的模拟,所以我可以在状态转换间进行任务分析处理。所以,我的调度器线程在这次作业依旧只是起到了一个消息传递员的工作。
1.3第三次作业
第三次作业指导书要求是多部多线程智能(SS)调度电梯的模拟,这次作业由三个不同类型的电梯组合,这三个电梯的运行时间,载客数量,可达楼层都不一样,这次的作业大大超出了我之前架构可扩展性的预期,导致了我在进行架构设计的大改。由于前两次作业保证了我电梯基础架构的正确性,所以基本结构并没有变化,线程还是三线程,调度器和输入处理线程通过reqlist
进行通信。由于这次的任务分配需要站到一个高点进行统筹规划,所以我将调度器线程作为这个高点,调度器可以拿到三个电梯线程的状态信息,由调度器线程对任务和电梯状态进行分析,然后由调度器线程把任务交付给各个电梯,电梯进行模拟运行。
由于这次作业的停止消息和拆分任务消息存在问题,因为我的停止线程操作是由输入处理线程发送一个从0层到0层的任务给调度器,然后输入处理线程停止,调度器接收之后把这个消息传递给所有电梯,然后调度器停止,电梯在接收到停止信息后,在处理完任务队列后由状态机停止线程工作。但是由于电梯的任务中可能包含换乘任务,第三次作业我的req
类中曾加了一个secondreq
信息,也就是换成信息,在当前任务完成后将换乘任务传给调度器,那么如果停止信息来了,有可能调度器先于换乘电梯任务完成之前先停了,所以我依靠与操作系统课的信号量锁mutex
解决了这个问题,这个mutex
限制了当前的任务完成状态,具体在bug
部分着重说明。
其实,我这次作业为了保证正确性第一原则,所以我并没有进行任务的特殊切割,所以其实我的调度器线程也只是一个拿到任务,然后进行一个粗略估算,把任务进行分配或者切割分配,电梯线程在拿到调度器线程给的任务后,再内部进行任务二次分析,电梯进行任务运行。
2基于度量分析程序结构
首先,由于我的程序架构是一个递增的过程,所以大体上层次不会有特别多变化,所以重点分析第三次作业的结构
2.1第一次作业和第二次作业
由于我第一次作业的设计考虑到了第二次作业,所以基本结构的方法都没有变化,这里统一分析。
UML类图
UML时序图
代码度量
method | ev(G) | iv(G) | v(G) |
---|---|---|---|
Elevator.Elevator(int) | 1.0 | 1.0 | 1.0 |
Elevator.excrunlist(int,int) | 1.0 | 1.0 | 1.0 |
Elevator.fetch(Req) | 1.0 | 4.0 | 4.0 |
Elevator.getCurf() | 1.0 | 1.0 | 1.0 |
Elevator.getDirec() | 1.0 | 1.0 | 1.0 |
Elevator.getRtof() | 1.0 | 1.0 | 1.0 |
Elevator.getStatus() | 1.0 | 1.0 | 1.0 |
Elevator.initmap() | 1.0 | 3.0 | 3.0 |
Elevator.isStar() | 1.0 | 1.0 | 1.0 |
Elevator.run() | 5.0 | 10.0 | 14.0 |
Elevator.runtime(long) | 1.0 | 2.0 | 2.0 |
Elevator.seekupdate() | 3.0 | 3.0 | 4.0 |
Elevator.seerun() | 1.0 | 2.0 | 3.0 |
Elevator.updatelist() | 1.0 | 9.0 | 9.0 |
Main.main(String[]) | 3.0 | 4.0 | 4.0 |
Req.getDirection() | 3.0 | 1.0 | 3.0 |
Req.getFromf() | 1.0 | 1.0 | 1.0 |
Req.getPid() | 1.0 | 1.0 | 1.0 |
Req.getTof() | 1.0 | 1.0 | 1.0 |
Req.initmap() | 1.0 | 3.0 | 3.0 |
Req.Req(int,int,int) | 1.0 | 1.0 | 1.0 |
Req.toString() | 1.0 | 1.0 | 1.0 |
Reqlist.add(Req) | 1.0 | 1.0 | 1.0 |
Reqlist.fetch() | 2.0 | 3.0 | 4.0 |
Reqlist.get(int) | 2.0 | 2.0 | 2.0 |
Reqlist.remove(int) | 1.0 | 1.0 | 1.0 |
Reqlist.Reqlist() | 1.0 | 1.0 | 1.0 |
Reqlist.size() | 1.0 | 1.0 | 1.0 |
Scheduler.run() | 1.0 | 3.0 | 4.0 |
Scheduler.Scheduler(ArrayList,Reqlist) | 1.0 | 1.0 | 1.0 |
Total | 42.0 | 66.0 | 76.0 |
Average | 1.4 | 2.2 | 2.533333333333333 |
class | ocavg | wmc |
---|---|---|
Elevator | 3.0714285714285716 | 43.0 |
Main | 4.0 | 4.0 |
Req | 1.5714285714285714 | 11.0 |
Reqlist | 1.5 | 9.0 |
Scheduler | 2.5 | 5.0 |
Total | 81.0 | |
Average | 2.6129032258064515 | 11.571428571428571 |
前两次作业复杂度较高的主要是电梯的run
方法和更新队列的updatelist
方法,第二个方法是电梯的中心方法,这个方法包括了下电梯的判断,上电梯的判断,电梯的最优主任务的判断,导致了这个方法十分长且功能繁多。Main和Elevator两个分别是主函数且包含了输入处理方法,另一个是电梯的类,这两个类复杂度较高,是因为我的电梯需要自身去判断任务的更新和排列,主函数需要创建各个线程。
2.2第三次作业
UML类图
UML时序图
代码度量
method | ev(G) | iv(G) | v(G) |
---|---|---|---|
Elevator.addf() | 1.0 | 1.0 | 2.0 |
Elevator.cancarry(Req) | 7.0 | 4.0 | 8.0 |
Elevator.Elevator(String,int,int,Reqlist) | 1.0 | 1.0 | 1.0 |
Elevator.excrunlist(int,int) | 1.0 | 1.0 | 1.0 |
Elevator.fetch(Req) | 1.0 | 4.0 | 4.0 |
Elevator.full() | 1.0 | 1.0 | 1.0 |
Elevator.getCurf() | 1.0 | 1.0 | 1.0 |
Elevator.getDirec() | 1.0 | 1.0 | 1.0 |
Elevator.getRtof() | 1.0 | 1.0 | 1.0 |
Elevator.getStatus() | 1.0 | 1.0 | 1.0 |
Elevator.isStar() | 1.0 | 1.0 | 1.0 |
Elevator.peo() | 1.0 | 1.0 | 1.0 |
Elevator.run() | 5.0 | 11.0 | 15.0 |
Elevator.runtime(long) | 1.0 | 2.0 | 2.0 |
Elevator.seekupdate() | 4.0 | 3.0 | 5.0 |
Elevator.seerun() | 1.0 | 2.0 | 3.0 |
Elevator.subf() | 1.0 | 1.0 | 2.0 |
Elevator.updatelist() | 6.0 | 14.0 | 15.0 |
Elevator.zhurenwu() | 5.0 | 7.0 | 7.0 |
Main.main(String[]) | 3.0 | 3.0 | 3.0 |
Req.getDirection() | 3.0 | 1.0 | 3.0 |
Req.getFromf() | 1.0 | 1.0 | 1.0 |
Req.getPid() | 1.0 | 1.0 | 1.0 |
Req.getsecond(Req) | 1.0 | 1.0 | 1.0 |
Req.getTof() | 1.0 | 1.0 | 1.0 |
Req.havasecond() | 1.0 | 1.0 | 1.0 |
Req.Req(int,int,int) | 1.0 | 1.0 | 1.0 |
Req.toString() | 1.0 | 1.0 | 1.0 |
Reqlist.add(Req) | 1.0 | 1.0 | 1.0 |
Reqlist.addmutex() | 1.0 | 1.0 | 1.0 |
Reqlist.addnull(Req) | 1.0 | 2.0 | 3.0 |
Reqlist.fetch() | 2.0 | 3.0 | 4.0 |
Reqlist.Reqlist() | 1.0 | 1.0 | 1.0 |
Reqlist.submutex() | 1.0 | 1.0 | 1.0 |
Scheduler.can(ArrayList,Req) | 3.0 | 2.0 | 3.0 |
Scheduler.choose(int,int) | 2.0 | 1.0 | 2.0 |
Scheduler.choose(int,int,int) | 4.0 | 2.0 | 4.0 |
Scheduler.cn1(Req,int) | 1.0 | 1.0 | 1.0 |
Scheduler.deliver(Req) | 1.0 | 34.0 | 34.0 |
Scheduler.init() | 1.0 | 4.0 | 4.0 |
Scheduler.run() | 1.0 | 5.0 | 5.0 |
Scheduler.Scheduler(ArrayList,Reqlist) | 1.0 | 1.0 | 1.0 |
Scheduler.split(Req) | 10.0 | 15.0 | 15.0 |
Scheduler.split2(Req) | 1.0 | 7.0 | 7.0 |
Total | 86.0 | 150.0 | 172.0 |
Average | 1.9545454545454546 | 3.409090909090909 | 3.909090909090909 |
class | ocavg | wmc |
---|---|---|
Elevator | 3.4210526315789473 | 65.0 |
Main | 3.0 | 3.0 |
Req | 1.25 | 10.0 |
Reqlist | 1.5 | 9.0 |
Scheduler | 5.6 | 56.0 |
Total | 143.0 | |
Average | 3.25 | 20.428571428571427 |
第三次作业设计我的调度器进行了大幅度的更新,导致scheduler复杂度过高,电梯由于设计也是自己进行任务的分析调度,写的时候没有很好的控制方法的细节,导致Elevator类也不是很好,这些设计细节我下个单元应该进行模块细化和整体性构建,避免过大的方法和类的出现。可以看出这次的设计与前两次有一个不同,就是Scheduler类的复杂度提高了,这是因为我的调度器需要实现高瞻远瞩的方法,需要对任务和电梯进行统筹规划,所以调度器的方法写了很多,包括拆分任务和找到最优分配的电梯,导致了复杂度有一个大的提升。
2.3SOILD原则分析
oo5 | oo6 | oo7 | |
---|---|---|---|
SRP-单一功能 | 电梯,调度器,输入处理线程各自完成各自任务 | 同oo5 | 同oo5 |
OCP-开闭原则 | 基础状态机搭建 | 没有对于run方法改动,只是对于调度刷新队列的方法进行重构 | 调度器进行分配任务方法在原有基础上的重构,电梯进行功能重构 |
LSP-里式替换 | 作业设计不涉及父子类问题 | 同oo5 | 同oo5 |
ISP-接口隔离 | 作业设计不涉及接口问题 | 同oo5 | 同oo5 |
DIP-依赖反转 | 不存在对类的抽象,所有都实例化 | 同oo5 | 同oo5 |
3分析程序bug
由于我没有互测环节,所以只进行论述强测和自己的测试。
3.1第一次作业
第一次作业由于是傻瓜调度,所以强测无错误出现,但是在调度器进行任务分配的时候采取了暴力轮询的方式,这点在第二次作业变成了错误点,需要改正,也反映了我一开始对于线程知识的欠缺。
3.2第二次作业
第二次作业我再进行测试的时候发现如果上限30十个任务同时输入的时候,由于是阻塞读入还有多线程的传递机制,导致可能我的电梯不认为是同一时刻的请求,在10个任务左右的接收就会开始运行,所以后续任务无法进行统筹安排,如果后几次任务为从1楼开始,那么电梯不会捎带,经与同学讨论,最终在电梯每次开始运行前进行线程sleep
10ms的时间,让可能的任务全部分配给电梯再开始工作(强测中有这样的点,同一时间全部输入,在后面的输入有1楼开始的任务)。强测虽然全部正确,但是强测我的分数不尽理想,因为我的调度挖了一个坑,我的捎带考虑的是只要在主任务的覆盖范围内,无论上行下行我都会把这个任务接上,但是由于设计的漏洞,导致可能最开始的任务使得整个队列进行一次上下的复杂运行。
3.3第三次作业
第三次作业我在整体写完后,发现测试点不能通过,思考后,发现如果有一个待拆分的任务前半段正在运行的时候,输入请求通知结束,那么我的输入处理线程会给调度器传递结束信息,这个时候调度器本身不存在任务,他会给电梯发送停止信息,但是它本身会直接线程停止,而这样电梯就无法把后半段任务传回调度器,导致了提前截至的问题,这个问题我通过了之前操作系统课学习的信号量锁解决了,也就是接到一个任务mutex
加一,差分一个任务mutex
加一,完成一个任务mutex
减一,只有当mutex
到0的时候输入线程才会把结束信息传给调度器,解决了这次作业的一个结构bug。强测最终没有出现错误,但是由于没有做很好的优化,最后得分也只有90多一点。
4心得体会
相较于第一单元的多项式作业,电梯的出现考验了我们对于多线程的理解,尤其是锁的概念尤为重要,无论是哪次电梯作业都靠考虑互斥操作的关系,怎么能尽量增加线程的独立性。另外就是电梯需要对于线程进行wait
和notifyall
操作,保证cpu时间,这就要去观察各个线程之间的联系,什么时候饥饿,什么时候满。当然,有了第一单元次次重写的教训,这一次的作业我在第一次就预留了大量的可添加模块,基本实现了模块化设计,所以我的第二次作业基本没有花费太多时间,虽然第三次超出了我的框架设计,但是经过基本变换之后整体的设计也没有大的变化,所以正确性有了很大的保证,但是恰恰就是第一次框架的设计,导致了后面我的优化无法进行很好的操作,受限于框架的固化,只能做一些基础的优化,这点也是我在下个单元需要着重注意的问题,怎么才能一开始设计出扩展性好的和优化可实行的框架,确实值得推敲。
BUAA_OO_博客作业二的更多相关文章
- BUAA_OO_博客作业一
BUAA_OO_博客作业一 (一)程序结构分析 1.代码统计 第一次作业 第二次作业 第三次作业 代码复杂度展示第三次作业的 method ev(G) iv(G) v(G) Constant.Cons ...
- BUAA_OO_博客作业四
BUAA_OO_博客作业四 1 第四单元两次作业的架构设计 1.1 第13次作业 类图 作业要求:通过实现UmlInteraction这个官方提供的接口,来实现自己的UmlInteraction解 ...
- BUAA_OO_博客作业三
1 JML语言总结 1.1 JML语言的理论基础 JML(Java Modeling Language)是用于对Java程序进行规格化设计的一种表示语言.JML是一种行为接口规格语言(Behavi ...
- 第十二周博客作业 <西北师范大学| 周安伟>
第十二周作业 助教博客链接https://home.cnblogs.com/u/zaw-315/ 作业要求链接https://www.cnblogs.com/nwnu-daizh/p/10831971 ...
- 个人博客作业Week1
个人博客作业Week1 一.问题 通读<构建之法>我有一下几个问题 PM没有参与代码编如何进行管理. 软件工程师的职业资格考试对我们来说很有必要吗. 当我们为用户开发软件时我们需要了解用户 ...
- java课程设计——博客作业教学数据分析系统(201521123083 戴志斌)
目录 一.团队课程设计博客链接 二.个人负责模块或任务说明 三.自己的代码提交记录截图 四.自己负责模块或任务详细说明 五.课程设计感想 (题外话,终于可以用markdown建目录) 一.团队课程设计 ...
- Java课程设计——博客作业教学数据分析系统(201521123082 黄华林)
Java课程设计--博客作业教学数据分析系统(201521123082 黄华林) 一.团队课程设计博客链接 博客作业教学数据分析系统(From:网络五条狗) 二.个人负责模块或任务说明 1.网络爬虫 ...
- C语言第七次博客作业--一二维数组
一.PTA实验作业 题目1:找鞍点 1. 本题PTA提交列表 2. 设计思路 定义n,i,j,ii,jj,a[7][7],flag,max 输入n for i=0 to i=n for j=0 to ...
- 2018上C语言程序设计(高级)博客作业样例
要求一(20分) 完成PTA中题目集名为<usth-C语言高级-第1次作业>中的所有题目. 要求二 PTA作业的总结(20分+30分) 将PTA第1次作业作业中以下2道题的解题思路按照规定 ...
随机推荐
- [NOIP2003普及组]麦森数(快速幂+高精度)
[NOIP2003普及组]麦森数(快速幂+高精度) Description 形如2^P-1的素数称为麦森数,这时P一定也是个素数.但反过来不一定,即如果P是个素数,2^P-1不一定也是素数.到1998 ...
- 57.部门职位管理 ExtJs 展示
1.jobInfo.jsp <%@ page language="java" pageEncoding="UTF-8"%> <script t ...
- Secure CRT中解决vim高亮设置的方法
此文主要是解决vim编程中高亮显示的.原因是: 1.默认情况下,SecureCRT是有自己的终端显示颜色.这样在我们编程中不利于阅读内容. 2.我们必须到Linux系统中进行改进才能真正解决这样的问题 ...
- web自动化测试—selenium游览器下拉框操作
# coding=utf-8'''下拉框实战思路导包:from selenium.webdriver.support.select import Select #下拉框select from sele ...
- Objective-C copy(转)
一.从面向对象到Objective-C概览copy 1.面向对象: In object-oriented programming, object copying is creating a copy ...
- CSS-类和ID选择器的区别
学习了类选择器和ID选择器,我们会发现他们之间有很多的相似处,是不是两者可以通用呢?我们不要着急先来总结一下他们的相同点和不同点: 相同点:可以应用于任何元素不同点: 1.ID选择器只能在文档中使用一 ...
- 暴力(python)
输出由1,2,3,4组成的互不相同且无重复的三位数! #方式一 lst = ['1', '2', '3', '4'] res = [] for i in lst: for j in lst: for ...
- Python随笔-函数
在Python中,定义一个函数要使用def语句,依次写出函数名.括号.括号中的参数和冒号:,然后,在缩进块中编写函数体,函数的返回值用return语句返回. def GetMax(v1, v2):#注 ...
- react native 报错日常 if (_total > 0) { ~~~~~~ ^ ~ 1 error generated.
node_modules/react-native/React/Base/RCTJavaScriptLoader.mm::: error: ordered comparison between poi ...
- Sql Server 优化 SQL 查询:如何写出高性能SQL语句
1. 首先要搞明白什么叫执行计划? 执行计划是数据库根据SQL语句和相关表的统计信息作出的一个查询方案,这个方案是由查询优化器自动分析产生的,比如一条SQL语句如果用来从一个 10万条记录的表中查1条 ...