304
社区成员
发帖
与我相关
我的任务
分享不知为何五一前没交上去,所以再交一遍。
在本单元的设计中,我采用了经典的生产者-消费者模型,并将“托盘”作为一个独立的线程安全类进行加锁。
globalQueue 和各个电梯的私有处理队列 processQueue)。synchronized (queue) 保护核心的增删查改逻辑。Thread.sleep 或网络/复杂计算)。例如,ShadowSimulator 对未来代价进行模拟计算时,属于耗时甚至迭代的逻辑,因此我们在同步块中只做一件事:深克隆(deepClone)当前状态。随后在同步块外部基于克隆副本进行耗时操作。wait() - notifyAll() 规范。在 InputTask 放入新请求后,仅对共享队列执行 notifyAll(),唤醒 DispatcherTask 使得同步块的职责非常纯粹:控制数据的可见性和原子性。我的调度器采用了独立的 DispatcherTask 线程。它充当了系统中的“中枢神经”:
InputTask 填充的 globalQueue 中提取请求(阻塞式 wait 直到有新请求)。ElevatorTask 对应的 processQueue 中。并且它还负责拆分和解析多段路径(如跨越 F2 换乘层的乘客),以及分发维护(MaintRequest)和双轿厢改造(UpdateRequest)请求。在此次作业中,由于需要兼顾时间和电量,我放弃了简单的分配原则,而是实现了影子模拟器(ShadowSimulator)。
LookStrategy 的基础运行逻辑,我为电梯设计了模拟器。当分配一个乘客时,调度器会利用 deepClone 复制所有电梯副本,然后分别“试运行”接下这个乘客的情形。在架构迭代的过程中,我也踩了不少坑,最具代表性的是以下两个多线程/深拷贝带来的问题:
Passenger 增加了 transToIdx 用于记录多段行程的目的地。但在 Passenger 的 deepClone 构造函数中,忘记了拷贝该属性。这导致 ShadowSimulator 在模拟时所有乘客的目标都退化为初始默认值(例如B4层),进而使得所有分配方案的 $\Delta \text{cost}$ 固定(如固定的4400ms)。这反映出即使不涉及线程冲突,对象快照的不一致也会让高级策略瞬间崩溃。UPDATE 状态的双轿厢转换 RTLE(超时):NORMAL -> UP_ACCEPT -> UPDATE -> DOUBLE 以及 GHOST 线程的启动逻辑)。在 ElevatorTask 处理相关状态机时,因为 MAIN 和 MINOR 的共享换乘层(F2)未正确处理避让和互斥,使得轿厢进入了互相等待或者频繁 sleep() 的死胡同,最终导致 RTLE。System.out.println(System.currentTimeMillis() + Thread.currentThread().getName() + " " + State) 打印状态转移,并通过不同标识符将 Dispatcher 和 ElevatorTask 的日志区分。UPDATE 流程,更容易捕捉死锁和逻辑循环。经历三次作业的洗礼,我对多线程架构和对象层次划分有了更深的理解:
Passenger, Elevator 属性)作为被动数据,由保护它们的“托盘”(如 RequestQueue)来集中做同步。工作线程不再彼此调用方法,它们只向托盘存取数据。这种解耦让线程安全更容易证明。open、close、move 状态机。UPDATE 状态机即可,做到了真正的“高内聚低耦合”。总结来说,多线程如同指挥一个乐团,线程安全是演奏不走调的底线,而优秀的层次设计和高度解耦则是演奏出华丽篇章(高分性能)的必要前提。