从上个月的三次作业有惊无险,几乎完美过关,顺带完成了OS实验,到这个月的三次作业全都功亏一篑,三个无效,还没时间做OS实验——如果忽略这是我的经历这一事实,我绝对会感叹人生无常,同时对这位同学报以怜悯同情之心,外加猜测这位同学这整月一定过得很舒坦,得此结果也算是报应不爽。
可惜啊,这就是我的经历,我没有资格去当旁观者,去感慨去怜悯去猜测。所幸,我还能平静地写下这份总结,还能冷静地思考一些我以前从未思考过的事情。关于思考感悟之类的东西暂且不提,且先把总结分析任务完成。
(1)多线程的协同和同步控制
第五次作业,请求管理器、调度器和电梯线程之间需要进行的协同和同步控制较少,需要着眼的地方在于请求管理器和调度器对请求队列的访问和修改,以及调度器线程和电梯线程对请求的访问和修改。这里我采用了flag信号进行控制,当一个线程在进行修改时flag为true,修改完成之后再将flag置为false;而只有当flag为false时,线程才能进行访问。
第六次作业涉及到的多个线程对同一内容(变量或对象)进行读写的操作较多,只采用设置flag方法就稍显不足了。所以,在这里除了设置flag之外,我还使用了任务转移和设置二级flag两个方法。任务转移,顾名思义,就是将某线程的一些任务交给另一线程进行处理,这里涉及的主要是对共享对象的修改任务。
设置二级flag稍显抽象,我们不妨看一个例子——线程A和B共同维护共享对象obj,共享对象中包含了许多元素;A遍历obj的元素,若某元素符合一定条件,则通知B对该元素进行某种处理;本次遍历中,A不确定符合条件的元素的数量和位置;另外,A希望遍历工作完成后,B再进行处理。这样就产生了一个问题——B是根据A设定的flag来判断是否有新任务产生的;而某元素触发flag产生时A不能立刻修改flag以通知B;当A能够通知B时,A已经在处理别的元素了,其所处理的元素未必会触发flag。如此,可引入二级flag,当某元素触发一级flag时,A将二级flag设置为true;遍历完成后,若二级flag为true,A在将一级flag设置为true,以通知B。
第七次作业涉及到的多个线程对同一内容(变量或对象)进行读写的操作也不少,我采用的协调方案与第六次作业的方案大体相同,稍有差别的地方在于,第六次作业侧重于设置二级flag,而第七次作业侧重于任务转移。
(2)基于度量的程序结构分析
第五次作业中,我只是注意到要控制每个方法的代码量,完全没有注意对各线程、各类的工作进行均衡化处理,这就造成了许多方法的操作极其简单、代码量极少,而重要的方法的操作则较多、调用的方法较多、调用层次较深。
另外,我的设计也并不符合显式表达原则,这体现在用多个数组存储一条request的请求楼层、请求时间、目标楼层等信息。这就造成了一些麻烦,比如每次传递一条请求的信息,都需要传递一堆参数。
要说明的是,因为时间有限,这份作业是没有完成的——在进行“极限操作”的时候,也许是因为脑子短路,我忘了让请求管理器把请求发送到请求队列,所以调度系统压根就“看不到”任何有效请求,那么调度电梯响应什么的就纯属扯淡了……
第六次作业原则上是要求每个触发器对应一个线程,但为了方便操作和管理,我采用的设计是——基于监控对象创建监控线程,然后每个监控对象再为其拥有的触发器创建触发器线程。如此,触发器线程只需要负责判断其对应的触发器是否被触发;而监控线程则根据触发器线程的触发情况进行相应处理。这次作业除了对方法代码量的控制,我还注意到了对方法调用深度的控制;但是我没有做到将输入处理部分从main中独立,所以Main类看起来比较臃肿。
毋庸置疑,既然这是一份无效作业,那么它就属于未完成的作业——我在处理输入的时候,把if条件写成了while条件,那么只要第一行输入不是结束命令,while条件就会一直生效,程序就会一直判断处理这行输入,至于第二行第三行什么的,对程序来说是不存在的,因为那根本都不会被读入的啊……
后来我又花了两天把它进行了补全、修改和优化,以上的UML图和复杂度分析图都是来着完全版。
第七次作业我不仅注意到对各线程、各类的工作量的均衡化,还遵循了显示表达原则等一系列原则,各线程之间的任务也比较清晰。但是,这依然是一份无效作业,因为程序的输出有问题——对于所有非无车请求,程序都正确处理并输出;但对于所有的无车请求,程序都没有输出处理过程。这也许是因为在共享对象的管理方面出现问题,因为在我的设计中,每个有车请求都有一张ProcessRecord,其中的部分内容是由调度系统记录的,而另一部分内容是由被派单的出租车记录的(其实这部分内容也可以由调度系统进行预判并记录,但这样就稍显“多此一举”);其中出租车对ProcessRecord的使用和管理也许出现了问题,导致调度系统无法完成对该请求的处理过程信息输出工作。
(3)程序bug分析
这三次作业中,我的程序都出现了致命性的bug,从而直接导致了作业无效。这些bug都对程序的基础功能起到了直接影响,而且很巧合的是,一个出现在了获取输入部分,一个出现在了有效输入结果传递部分,一个出现在了输出部分……
之所以会接连出现这样的bug,应该是因为状态问题和习惯问题。第五次作业,周日、周一和周二这三天,我总共睡了5小时,状态会是如何就不多说了;第六次作业,三天8小时,比起第五次也是不遑多让;第七次倒是好一些,可惜第七次作业的出租车管理器部分,我是在周三“凌晨”6点写的……当然,如果我的习惯够好,也不会出现这样的问题——可惜这个如果暂时不存在。每次完成一个类或者一个方法,我都不会怀着“这块内容保证不存在问题”的思想去做,而是想着“先按现在的思路写完,如果有bug在调试的时候会很明显的,到时候再改”。我不知道编程习惯跟我差不多的同学有多少,但我知道这样的同学的debug过程一定会漫长而痛苦。此外,如果情况紧急时间不足,那又会发生什么呢?
(4)互测分析
第五次互测,我当时只想着回血(补觉),所以也就没有仔细阅读我分配到的代码,而只是在一些设计细节方面进行了测试。
这是因为基于前几次分配的结果,我发现编程能力好的同学,其设计在基本功能的实现上可以说一定是完美的,也就是说其程序不会出现有的捎带没有实现、有的输出时间不对或者输出顺序不对之类的问题。但其对基本功能的实现越是完美,在细节上就越有可能出现问题,这些问题属于细枝末节上的问题,很容易被这种级别的同学忽略。到底是怎样的问题,还要基于具体情况分析,比如我第一次匹配到的那份疑似出自竞赛党之手的代码,这位同学把第一个多项式取出来进行单独处理,其余的多项式一起处理——但他忽略了一种情况,那就是输入只有两个多项式,并且都只含有1个0次项(也就是只有1个常数)。但是,毋庸置疑,这需要仔细阅读对方的代码,否则如此细微的问题是不可能被发现的。
如果分配到的是“巨灵神”级别的同学的代码,而测试者又想在粗略阅读甚至不阅读对方代码的情况下找到bug,也不是不行。这一样是在细节上着手,只不过这里的细节是结构细节而非代码细节。第五次互测我就是这样做的。只不过这就只能碰运气了,不像上面那招一样百试百灵,可以“吃遍天”。针对结构细节进行测试,大概指对最低临界情况(单个有效输入或直接输入结束命令)、最高临界情况(最大边界输入或最大+1或最大+N*最大)和特殊输入(有效+特殊无效)、(结束命令+有效输入)等情况的测试。
关于代码细节和结构细节的定义,我不确定我的定义是否合适而准确,我对此做出定义,只是为了方便区分两种在细节上进行测试的方法——其一是仔细阅读其代码,识别其在处理细节上可能会出现的问题;另一则是大体阅读甚至不需要阅读代码,直接对某些已知的或公认的细节进行测试。
再说说第六次和第七次互测。这两次我都没有测试对方代码——第六次是因为我当时需要补全和修改自己的程序;第七次是我忘了,想起了的时候已经是10点多了,对方代码早凉了……
(5)心得体会
三次作业下来,我的提升还是有的,比如不会再写那种百余行的方法、不会再把一个请求的信息切割成几块然后分别存储。但行文至此,是时候来点悲惋气氛了。
三次作业,全都是玩命去写,结果都是无效,最后直接保送补给站——哦,不能这样说,应该说最好的结果就是补给站,如果再多来几次似乎连进补给站都没资格。如此行为终得如此结局,哪怕我再平淡再无争再无谓,也难以说服自己,让自己心中不失望不悲凉。我该如何告知那个每周三早上,脸甚至开始明显变绿的自己——嗨,小子,看到没,你这周的努力就是徒劳的,看清楚没?这下服了没?你的挣扎是徒劳的!
我无法这样告知那样拼命的自己,但事实帮我做了。我要告知的,仅仅是你其实略有收获,还有,你需要休息,以及反思。
我需要休息,这毋庸置疑。有此遭遇,若是不好好缓缓,而是立刻知耻后勇、立刻精神抖擞地去学习、去拼搏,如果我真这样做了,那才是真的让自己彻底失望。我不是超人,哪怕我自认为毅力过人、无畏绝境,但也不能如此压迫自己,否则疯掉就是唯一结局。但这样倔强、这样无畏绝境的我,面对如此局面,也不需要哭哭啼啼,不需要埋怨别人“太狠”,更不需要去质疑自己是否真的无能。我需要的,仅仅是休息会儿,喘口气,缓缓心情,然后看看之前的结果,仅此而已。
我更需要反思。第五次作业恰好处在清明节前后,可我在清明节却浪了好几天,直到假期快要结束才开始着手去做。舒缓3月份的压力,这无可非议;可是我在明知第五次作业有难度的情况下,还玩什么“今朝有酒今朝醉”,那就属于作死了。这次教训也告诉我,舒缓也要看情况看方式,否则任你有天大的理由,也难免会造成不好的结局——事实不是人,它不需要讲人情讲道理。不过第六、第七次作业,我开始做的时间其实和其他同学差不多,可依然无效了,这就不是上面的理由可以解释的了。这其实跟我的个人特点有关,我的特点大概是“不善于速,而极于韧”“不善于受,而极于用”。具体来说,做某件事,我通常比别人做得慢,但无论如何,我都会坚持去做,并且坚持到底;学习某些东西,我学习得比别人慢,但学会之后,我会运用得比很多人都要更好。只是,OO作业要求的恰恰是要学得快、做得快,如果我不想挂,就只能提前去做、花更多的精力去做。
原文地址:https://www.cnblogs.com/1606-1054/p/8969797.html