20373222李世昱第二单元总结

第二单元博客总结

第五次作业UML类图和时序图如下:

        由于第五次作业整体比较简单,该次作业并无过多迭代性考虑,采用的是标准的生产者消费者模式,INPUT线程不断读入新的数据发送给调度器,调度器把符合对应楼座的请求发送给对应的电梯,剩下的交给电梯自己不断处理。

        同步性控制:第五次作业共享数据比较少,所以采用线程安全类的方式进行同步控制,两个线程传递请求的时候都通过一个Queue的类,发送者调用add函数发送信息,并且会唤醒可能处于等待中的接收者线程,接收线程只能通过Queue提供的get函数获得信息,如果没有信息则会陷入wait 以此避免轮询并释放锁。采用的锁即这个Queue对象。

        调度器策略:很简单的分楼层调度

  电梯策略:经典LOOK算法,相信别人博客已经尽述,再次不再赘叙

   迭代性考虑:暂没考虑

   BUG:在电梯满员之后如果该楼层有人仍要上楼,电梯会空进行开关门而不交换人员,导致性能很慢被卡超时,后续实验中修复.

  数据生成策略: 该数据生成程序是在第一次作业结束后做成的,不对正确性做检查而尝试怼电梯不同策略有明显时间区分.首先生成一个A1-A10的基本请求,然后尝试依次生成三种类型的请求:1 捎带请求,该请求会在基本请求的电梯可能到自己的时间段(因为之前的楼层可能会开关门耽误400,所以时间范围是一个时间段)中随机的时间点发送一个可以被捎带的请求,该请求会随机若干次, 2.卡人口请求:该请求会在基本请求同时不断塞入不同出发地一样目的地有所差别的请求来尝试占据电梯的人口.3.逆向请求,在基本请求目的地附近生成与基本请求相反方向的请求来增加反方向的请求.这其中的每个请求都会作为基本请求继续迭代若干次,每个种类的同种迭代次数会大幅度减少,迭代3-4次 .以上数据我用自己的LOOK算法和官方时间包比用时,时间差距最多可达14%(标程每次运行时间基本超过50s).

  由于本人第二次作业完全为了第三次作业考虑而 特地重构的架构,所以第二次第三次作业电梯的架构和线程交互模式完全一样,(也就是第二次作业已经可以支持换乘了,第三次仅仅是略微增加可定制),所以在此一并展示.

      

 

整体的工作流程:

  INPUT线程把拿到的数据交给中央控制器,控制器那里有LIFT自更新的数据(也就是每个LIFT每次移动都会汇报给control的这个部分),然后控制器根据一定的算法把现有电梯的运行情况转化成一张50个节点的无向图,每个人把这个图存入自己中,然后自己根据这个图运算出下一步应该去那里,控制器根据这个人下一步要去的地方交给合适的电梯,电梯送达之后会把运送的人当前位置修复为目的地,然后重新交回控制器,控制器如果拿到的人出发地等于最终目的地,则说明这个人已经到达,直接删除这个人.新加入电梯时,已经分配的人不会在选择这个电梯,采用的方法就是把所有人重新取出重新绘图重新分配.

同步控制:

  这里的共享数据,除了在不同线程中传递人仍然采用线程安全类Queu以外.LIFT自更新和新加入电梯的全部重新分配会导致控制器中的MESSAGE和电梯内部的处理队列会被共享,采取的解决方式是利用高效的线程安全容器,即不锁很多导致运算缓慢,也不会导致线程不安全

调度器算法:

  先在此说明,如果是学弟学妹偶然看到这个文章,请不要轻易尝试采用这种算法,不仅容易出bug,调参玄学,性能也很玄,建议另找一篇自由竞争的架构学习!!!

  算法采用的是flyod全源最短路算法,复杂度n^3,由于这里n很小只有50,时间还可以接受,但是楼层更高的时候可能会很影响时间..首先建立一张完全不连通的图,然后去遍历所有电梯,给对应电梯能联通的节点的边赋值权重,大小通过电梯的信息用价值评估函数评估出来,这样全部电梯加入之后得到一个无向图,然后调用算法找到一条最短路,作为该乘客的换乘路线,这样也顺手解决了第三次作业横向电梯部分不可达的问题,不可达就不会建立边,最短路径当然不会那么走.

  算法好处就是,基于算法的乘客可以采取很多超出你想象力的策略移动,一些对于其他架构很难写的策略这个算法自然就做到了,比如连续多次换乘: 乘客1A-10到C-8 新增两台电梯 分别是10层AB电梯和9层BC电梯,乍一看的最优策略就是A10->B10->B9->C9->C8,实际运行出来乘客也是如此移动的.

  上述优点看起来很美好,但是存在很大的问题,没有考虑电梯所在的楼层,上述算法虽然做到了乘客占据电梯时间最短,乘客最快到达,却没有考虑电梯到达乘客需求的时间,实际我的程序跑上面的代码在ABC发送请求电梯都是从A1B1C1开始移动的,实际上非常非常慢,甚至远不如在1楼换乘.

  我们将数据分为两段,前端的大量数据和末端的一些数据,在处理前端大量数据时因为每个乘客占用电梯时间都是尽可能少,性能还是可以,但在处理最后的末端数据就可能使得缺点暴露的很明显.我的没有实现的想法是能否分段处理数据,最后一部分数据换一种算法让他们采取的策略是让电梯运行时间最短而不是自己路径最短,

  上述算法性能表现大概是92分,很多数据点要不然就是9899要不然就是85,我也在课下跟一位最终得分99.8的采取自由竞争的大佬对拍一下运行时间,比对中发现另一个问题:价值评估函数完全是凭自己连蒙带猜写出来的,不同参数有很大影响(参数可能过拟合),在一些数据点我更改参数之后性能比得分99.8的程序还要快出20%,但是另一些数据点这组参数表现就很差,会慢出15%左右,总而言之调参调的很玄学.

  最后如果有大佬采用的类似的算法并且最终性能表现得还不错还望不吝赐教

BUG:

  第二次作业并无bug,第三次作业由于打字错误 把fina打成from(都是f打头,自动补全的锅),导致一个隐蔽的bug没有被我测出来,交上去的时候挂了三个点被hack4次.我的电梯运行时不检查能否开关门,因为相信算法算出来的一定是符合可达性的要求(事实确实如此),但是由于打错变量导致一些不可达的请求错误的给了某个楼层从而导致横向电梯错误的开关门.把名字改回来就全部修复了.

  第二三次的数据生成器仅仅是增强了第一次的数据生成器的范围,并无做过多改进

 

心得体会

  这是第二次因为打字错误而不是程序内部或者线程安全导致的bug了啊啊啊,上一次是第三次作业,丢了这些分非常痛心,这告诉我数据生成一定要尽可能全面覆盖.本单元的多线程的实践也让算是让我对多线程有初步了解了,本单元没出现过因为线程安全而导致的bug.一些共享数据推荐封装入线程安全类中,这样能大大减少出问题概率.在处理轮询问题时,我并没有思考在那里去加wait,而是让这个线程去有意调用空的共享对象(不空他也不会wait)的get函数自然的陷入wait,并在之后的设计中再也没出现过轮询的问题.

 

posted @ 2022-05-01 14:17  zdfwqc  阅读(27)  评论(0编辑  收藏  举报