309
社区成员
发帖
与我相关
我的任务
分享第二单元围绕电梯调度考察多线程编程。
同步块应该力求精简,其设置的粒度越细,程序的并发效率也就越高。
有的时候可以舍弃一些绝对安全的写法,来进一步提高性能。
例如,第三次作业中,对于一部电梯是否进入了双轿厢状态,我设置了一个doubleFlag变量来做标识;在电梯让位的时候需要判断doubleFlag是否为真,如果为真才进行让位。
看完上述表述你会发现,在电梯让位过程,从判断到改变电梯状态,最后到输出ARRIVE,这一整套下来doubleFlag是不能被外部写入的,不然可能会出现电梯已经变回单轿厢状态后还进行让位的情况。所以我们需要对doubleFlag上锁————但是真的有必要吗?
可以预见的是,这个并发安全只是由两个线程之间的RAW读后写问题引发的,一是涉及的线程数目少,二是出现这个情景的机会并不多;我们只需要在出现问题之后进行一定的补救措施,把电梯的状态改回来即可。这就好比一个CPU执行分支指令的时候,不可能让分支指令后面的指令阻塞到分支指令出结果才执行,而是在分支预测失败之后,对流水线进行冲刷即可,无事发生。
大部分地方用对象锁就可以。对于双轿厢模式下二楼的管理,我让电梯进入的时候获取对于二楼的锁f2Monitor,离开二楼的时候释放即可;这个实现使用了java内置的Lock对象。
同步块中的语句应尽可能减少对其他锁的获取,不然调试死锁问题会比较苦恼。
汗。。。我的调度策略就对每个电梯均匀分配的。
整体的生产者-消费者模式类似:
InputThread --RequestQueue--> DispatcherThread --ProcessingQueue--> ElevatorThread
调度器DispatcherThread从请求队列获取任务,然后分发给各个电梯的处理队列中。
从输入线程InputThread的结束开始,各个线程逐级结束。调度器DispatcherThread接受到InputThread的结束信号,如果满足一定条件,则对电梯线程发送结束信号。
平均分配的好处是性能差不到哪里去,对于正在进行检修、升级和回收的电梯,他们最多7s不能处理乘客请求,不会出现一直处理不了导致最终超时的情况;所以不需要对这几个状态进行特判,无脑平均即可。
第二次作业有人做了对于检修的特判,然后被奇妙数据点hack了。这个数据点让五个电梯全部检修,然后塞50个请求,于是这50个请求都跑到了同一个电梯里面,最后超时了。
好像最卷的策略是影子电梯,搞一个模拟器看哪个电梯完成它自己的任务需要的时间最短。但是我摆了。
bug调试好像没有想说的,面对并发问题还就是那个print大法。轮询的时候,终端出现一串输出;出现并发问题的时候,检查输出出的相关变量。
从第二次作业我开始思考层次化设计。具体情景如下:
第一次作业的时候,我的架构类似:
InputThread --RequestQueue--> DispatcherThread --> ElevatorThread
结束的时候,从InputThread开始顺序结束,非常自然。
不过,第二次作业新增了一个要求:维修的时候,电梯需要把所有的乘客回流到前面的层级,进行重新处理。上面的架构就略显奇怪了——具体应该回流到哪里呢?好像只有RequestQueue满足要求。但这个方案并不理想,原有的层级就全都被破坏了。
于是乎,在DispatcherThread和ElevatorThread加上一个缓存,这样就解决了!所有的回流请求都可以被回流到这个缓存里,如下:
InputThread --RequestQueue--> DispatcherThread --ProcessingQueue--> ElevatorThread
如何优雅地结束呢?我的方案是维护一个全局变量requestCount,来表示还有多少剩余任务。那么这个变量应该存在哪里,如何处理并发问题呢?
建立一个全局单例类存储requestCount,并为其创建原子的setter和getter方法。在多线程的情境下,这个变量的修改并不会被及时写回主存,被其他线程读取到最新的值;所以为其添加volatile关键字。
如何处理换乘层的撞车问题呢?需要一个高于电梯层级的类来管理主副轿厢的通信。于是增加一个电梯井的层级,在其中管理换乘层的锁f2Monitor。电梯进入换乘层之前,向电梯井申请f2Monitor;离开换乘层的时候释放f2Monitor。
锁不应该被暴露给外部,应该利用锁去封装public方法。具体到这个例子中,电梯不应该直接持有f2Monitor的引用,而是应该调用电梯井中类似intoF2()、leaveF2()的方法。
我个人对大模型的使用还是相对保守的()。听说现在大家慢慢在搞工程化、可验证的ai编程,而非简单的提示词工程了;我觉得我对ai的使用能力还不过关,所以在作业中,如果时间允许的话,我还是倾向于把ai当做一个知识顾问。
比较有趣的是,前两次作业,因为我的个人原因,基本都是vibe coding的。最后强测很惨。第三次作业的时候重构了所有的东西,一次性写了三次作业的内容。。。最后进a房了,也算有一个比较好的结果吧。
被多线程打蒙了,不过感觉这次第二单元还是比往年要简单了点。。。大家一起挑战一个难题是一件很有意思的事情,受益匪浅。感觉指导书和测试体验都不错,希望助教组再接再厉吧。