2019-OO-第二单元总结

多线程电梯调度问题

思路综述

第一次作业

第一次作业是非常简单的傻瓜电梯,不需要考虑容量,不需要考虑调度策略,运用了基本的生产者消费者模型,而且生产者消费者模型也一直贯穿了三次作业。

第二次作业

按照指导书写了ALS调度方法,在elevator的run方法中,楼层每变化一次,都先判断电梯中是否有人(runlist),如果有人,则取第一个人为主请求,目的地即为这个人的tofloor,否则取电梯外的第一人(requestqueue),目的地即为这个人的fromfloor,根据得到的目的地判断电梯运行的方向(这里出现了问题,遗留到了第三次作业导致强测CPUt了QwQ)。然后在每层楼判断是否有目的地同方向的人,是否有要出去的人。最后电梯运行一层楼。

第三次作业

单部电梯的运行和第二次作业中基本一样, 除了限制了容量,在进人的时候需要加一个判断。本次作业的难点(对于不优化的我而言)应该是请求的拆分。请求的拆分,主要问题在于如何找到最短的拆分,和保持拆分后的时序,即一个请求的前半段拆分送达后,才能执行后半段拆分。对于前者我的实现是把三个电梯23层楼,类比成一个23*3的迷宫,然后用bfs来解决,与普通电梯不同的是,对于同一个电梯,即迷宫上的同一列,即使上下的两格标记为1(障碍,不可达),该电梯也是可以经过这一层的,只不过不可以在这一层换乘,故而判断条件有所变化。
上下移动:

左右移动:

对于后者,我创建了一个MyRequest类继承PersonRequest,多了一个state属性,当且仅当state为0的时候,这个请求可以被电梯接入,而当每个请求出电梯的时候,就遍历后面的请求,找到id一样的第一个请求,将它的state设为0。

代码分析

结构分析

第一次作业




整个程序都很简单,只有elevator的run方法数据偏高。

第二次作业




和第一次作业一样,程序思路比较简单,没有很复杂的方法,elevator的run方法数据偏高。

第三次作业



可以看到bfs方法的结构化程度、循环复杂度都遥遥领先,而Bfs类的类方法的平均循环复杂度也当仁不让地排在了第一,电梯线程类和调度器类的方法总循环复杂度也非常高。

bug分析

前两次作业没有什么bug,只不过第二次作业的优化性能分较低。第三次作业出现了CPU tle的问题,检查了很久很久,一直在while循环体里找,看是否发生了轮询,经过了漫长的printf大法之后,发现是一个惊天大bug,这样看来强测只炸两个点都是我欧。问题出现在Schedule类中的get方法,在原来的get方法中,我遍历整个队列,如果找到符合要求的,则返回该请求,找到null,则返回null,否则wait循环,但是wait被唤醒过后,我返回了requestqueue.get(0)……这简直毫无道理了,即会导致电梯跑到它不该到的楼层一直等待。
为解决这个问题,我把get方法分离出一部分即getresult方法,在这个方法里,如果符合要求,就返回符合要求的request的索引+1,null则返回-1,否则返回0,在get方法中,如果getresult方法的返回值为0,则等待,被notify后重新调用getresult方法,while 返回值为0,则一直循环,直到返回null或者符合要求的request。

风格分析

比较喜欢我的第二次作业的作业风格,在elevator类中,每一个方法都比较短,都有自己独立的功能,结构层次比较清晰,容易理解。而第三次作业就存在了哪里有问题给哪里加补丁的情况,显得非常丑陋,甚至还曾经把bfs的迷宫放在scheduler类里,不过最后还是单独为它构建了一个类。

Hack Hack Hack

这次互测还挺不爽的,这老哥简直不顾OS期中考试,仿佛定了闹钟一样精准提交测试样例,然而到最后一波修复就解决了……bug列表 从来没有这么长过。

当然我们在跑评测机的时候,很少会去研究对方的代码,检查是不是同质错误,但我觉得提交二三十次,狂刀别人的做法还是没有什么必要,不过的确监督了我找bug。

难点总结

线程安全

在这次作业中,出了很多线程不安全的bug,在debug的过程中才逐渐对线程安全和线程不安全有了一些认识。比如说经常出现的一个问题:IndexOutOfBoundsException异常,这是因为Arraylist是线程不安全的,而我又没有锁成功。
最后感谢晶巨分享的一篇文章,

我们观察发生越界时的数组下标,分别为10、15、22、33、49和73。结合前面讲的数组自动机制,数组初始长度为10,第一次扩容为15=10+10/2,第二次扩容22=15+15/2,第三次扩容33=22+22/2...以此类推,我们不难发现,越界异常都发生在数组扩容之时。
由此给了我想法,我猜想是,由于没有该方法没有同步,导致出现这样一种现象,用第一次异常,即下标为15时的异常举例。当集合中已经添加了14个元素时,一个线程率先进入add()方法,在执行ensureCapacityInternal(size + 1)时,发现还可以添加一个元素,故数组没有扩容,但随后该线程被阻塞在此处。接着另一线程进入add()方法,执行ensureCapacityInternal(size + 1),由于前一个线程并没有添加元素,故size依然为14,依然不需要扩容,所以该线程就开始添加元素,使得size++,变为15,数组已经满了。而刚刚阻塞在elementData[size++] = e;语句之前的线程开始执行,它要在集合中添加第16个元素,而数组容量只有15个,所以就发生了数组下标越界异常!

来源于https://blog.csdn.net/u010010428/article/details/51258783

请求拆分

请求拆分的方法有很多种,先后考虑了地铁线换乘的模型(上学期数据结构作业题),即用Dijkstra最短路径算法,和BFS迷宫模型两种方法。后来还是选择了BFS迷宫模型,写起来比较容易。

wait/notifyAll

在我写第三次作业之前都没有搞清楚notifyAll和wait的作用,加了很多notifyAll避免线程一睡不醒,但实际上并不需要,总的来说,scheduler的get方法里需要用wait,如果电梯暂时没有可以执行的请求,且没有读取到null的时候,就让它wait,在add request的时候要加notifyAll,唤醒wait的线程。

感想

总的来说感觉不是很理想,出现了面对中测编程的情况,没有主动去编一个评测机,以致于自己的bug也没有好好找,在hack别人的时候也显得很无力(尤其是被别人hack几十次想反击一波的时候),还有关于线程安全、死锁等问题也没有仔细思考,而是等问题出了再去找寻解决方案。相比第一单元而言,作业变复杂,而自己反而变得有些松懈,不太ok。

2019-oo-第二单元总结的更多相关文章

  1. oo第二单元作业总结

    oo第二单元博客总结 在第一单元求导结束后,迎来了第二单元的多线程电梯的问题,在本单元前两次作业中个人主要应用两个线程,采用“生产者-消费者”模式和共享数据变量的方式解决问题.在第三次作业中加入多个电 ...

  2. OO第二单元优化博客

    OO第二单元优化博客 第五次作业没有性能分,但是,我在这一单元的宗旨就是写一个日常生活中 最常见的那种电梯,所以第五次我没有写傻瓜电梯,而是直接写了个\(look\),和第六次基本相同. 总计一下lo ...

  3. 【OO学习】OO第二单元作业总结

    OO第二单元作业总结 在第二单元作业中,我们通过多线程的手段实现了电梯调度,前两次作业是单电梯调度,第三次作业是多电梯调度.这个单元中的性能分要求是完成所有请求的时间最短,因此在简单实现电梯调度的基础 ...

  4. OO第二单元小结

    OO第二单元小结 一.三次作业代码分析. 1.第一次作业 第一次作业是单部电梯的傻瓜调度,由于其过分傻瓜,所以第一次作业我只有两个类,一个main,一个电梯,main类负责不断从输入流中读取命令,如果 ...

  5. OO第二单元多线程电梯总结

    OO第二单元多线程电梯总结 第一次作业 设计思路 Input为输入线程,负责不断读取请求并将读到的请求放入调度器中. Dispatcher为调度器,是Input线程和Elevator线程的共享对象,采 ...

  6. 电梯也能无为而治——oo第二单元作业总结

    oo第二单元作业总结 一.设计策略与质量分析 第一次作业 设计策略 在第一次作业之前,我首先确定了生产者--消费者模式的大体架构,即由输入线程(可与主线程合并)充当生产者,电梯线程充当消费者,二者不直 ...

  7. 2020北航OO第二单元总结

    2020北航OO第二单元总结 前言 本单元考察基于多线程的电梯调度问题,成功让我从一个多线程小白到了基本掌握了使用锁来控制线程安全的能力,收获颇多(充分体验了迷茫地de一个又一个死锁bug的痛苦). ...

  8. OO第二单元——多线程(电梯)

    OO第二单元--多线程(电梯) 综述 第二单元的三次联系作业都写电梯,要求逐步提高,对于多线程的掌握也进一步加深.本次作业全部都给出了输入输出文件,也就避免了正则表达式判断输入输出是否合法的问题. 第 ...

  9. OO第二单元作业总结【自我反思与审视】

    第二单元作业的完成史,就是一部心酸的血泪史…… 多线程的出现为我(们)打开一片广阔的天地,我也在这方天地摸爬滚打,不断成长!如果说第一单元之前还对Java语法有所了解的话,那么这单元学习多线程则完全是 ...

  10. OO第二单元(电梯)单元总结

    OO第一单元(求导)单元总结 这是我们OO课程的第二个单元,这个单元的主要目的是让我们熟悉理解和掌握多线程的思想和方法.这个单元以电梯为主题,从一开始的最简单的单部傻瓜调度(FAFS)电梯到最后的多部 ...

随机推荐

  1. liunx redis集群添加密码

    第一种方法: 修改每个节点redis.conf配置文件: masterauth 123456 requirepass 123456 各个节点的密码都必须一致,否则Redirected就会失败 重新启动 ...

  2. @PathVariable出现点号"."时导致路径参数截断获取不全的解决办法

    @PathVariable出现点号"."时导致路径参数截断获取不全的解决办法 比如,我路径是/test/{name},name的值是1.2.3.4,后台用@PathVariable ...

  3. css_base_note

  4. 想对list里面的对象进行排序

    不必使用排序算法.实现Comparator接口就行

  5. python 3 实现文件下载的方法总结

    新学的python,兴奋不已,于是网上各种资源各种爬,发现对于同样文件下载,各个下载方法的速度是不一样的(目前就知道两种方法下载#尴尬) 下面分别看下吧: 下载同样的图片,方法二的速度是方法一的二倍 ...

  6. web.xml配置文件的简单说明

    简单说一下,web.xml的加载过程.当我们启动一个WEB项目容器时,容器包括(JBoss,Tomcat等).首先会去读取web.xml配置文件里的配置,当这一步骤没有出错并且完成之后,项目才能正常的 ...

  7. python学习:输入中文

    输入中文 python2 输入中文:#!-*- coding:utf-8 -*-(或者#coding:utf-8)例如: msg = u"我爱北京天安门!" print msg p ...

  8. NOIP-数字反转

    题目描述 给定一个整数,请将该数各个位上数字反转得到一个新数.新数也应满足整数的常见形式,即除非给定的原数为零,否则反转后得到的新数的最高位数字不应为零(参见样例2). 输入描述: 一个整数 N. 输 ...

  9. wangEditor大图片上传问题

    wangEditor上传大图片时候会 上传超时.后端没有问题,我百度很多,又去群里问,都得不到答案.最后问同事,有个属性  editor.config.uploadTimeout = 10000000 ...

  10. Qt中在UI文件中新建控件并命名,但在代码中无法识别UI中的控件?

    代码中添加FilePathLineEdit控件,显示标准文件选择对话框显示选择的文件路径,但在槽函数中ui->FilePathLineEdit->setText("FilePat ...