
结论SetOfflineAsync的 UI 卡顿主要不是async失效而是其中仍存在同步等待和同步硬件调用。设备断电后底层 Socket 读取或重连会等待超时调用链最终阻塞 WPF Dispatcher。已定位的问题SelectMode在进入关闭模式前先同步调用StationControlViewModel.Mode.csStopAutomaticRealtimeAcquisition();该方法内部使用StationControlViewModel.Realtime.csStopAutomaticRealtimeAcquisitionAsync().GetAwaiter().GetResult();实时线程如果正执行_loadUnit.ReadStatus()或其它同步 Socket 读取取消任务不能立即中断底层同步读取UI 会一直等待。SetOfflineAsync内部又重复同步停止一次StationControlViewModel.Safety.cs这会导致同一次关闭流程最多发生两次等待。RFID 工装次数写回虽然方法名是FlushToRfidAsync但底层读取是同步执行的RFIDController.csReadWithRetryAsync中直接执行read()如果 RFID 已断电这段同步读取会在 UI 线程上等待超时。该操作发生在 PLC 连接判断之前。PLC 判断只检查对象是否为空StationControlViewModel.Safety.cs断电后对象可能仍然存在但IsConnected false。此时仍会尝试执行三条 PLC 指令(plc.WriteNonForceLock(关闭),plc.WriteRunLightOff(关闭),plc.WriteAllTowerLightsOff(关闭))元组表达式会无条件执行三次写入即使第一条已经失败也会继续等待后续通讯超时。HasConnectedHardware只判断驱动对象是否存在不代表设备当前在线StationControlViewModel.Safety.cs推荐优化计划先消除 UI 线程同步等待将以下调用全部改为异步等待awaitStopAutomaticRealtimeAcquisitionAsync();重点修改SelectMode中的关闭模式分支SetOfflineAsync内部StartAutomaticRealtimeAcquisition等其它 UI 调用路径。最好删除或限制使用同步包装方法StopAutomaticRealtimeAcquisition()这样即使后台实时线程还需要等待一次设备超时UI 线程也不会被阻塞。RFID 写回前先判断连接状态关闭模式时if(devices?.RFID2?.Connectedtrue){// 执行工装次数写回}else{// 跳过写卡保留本地 PendingCount并记录日志}同时将 RFID 写回放入后台线程awaitTask.Run(()_fixtureUsageService.FlushToRfidAsync(devices,切换关闭模式,CancellationToken.None));关闭模式下工装次数写回属于追溯动作不应阻塞安全离线。设备断电时保留本地待写次数后续设备恢复后再补写。PLC 使用IsConnected预检查PLCController?plcdevices?.PLCController;if(plcnull||!plc.IsConnected){errors.Add(辅控PLC未连接跳过安全输出写入。);}else{// 执行 PLC 安全关闭}注意PLC 未连接时仍要继续执行ApplyOfflineState()自动化 Close 通知DisconnectStationDevicesAsync()不能因为 PLC 断线直接return否则旧 Socket 和后台轮询可能无法释放。PLC 指令改为失败即停保留原有顺序但不要使用无条件执行的元组intunlockplc.WriteNonForceLock(关闭);if(unlock!0||!plc.IsConnected)return;intrunLightplc.WriteRunLightOff(关闭);if(runLight!0||!plc.IsConnected)return;inttowerplc.WriteAllTowerLightsOff(关闭);这样 PLC 断电时只产生一次有效失败不会连续等待三次通讯超时。主控板同样增加连接判断devices.MainBoard ! null只能说明对象存在还应判断devices.MainBoard.IsConnected主控板断电时跳过测试使能关闭和高压关闭写入记录安全停机失败继续离线和释放连接。ExecuteMainBoardAsync已经在线程池执行不需要再嵌套Task.Run。区分“设备对象存在”和“设备已连接”建议保留两个概念HasHardwareObjects决定是否需要释放旧设备对象ConnectionSnapshot决定是否可以发送硬件命令。不能用“对象存在”作为“设备在线”的判断。释放动作继续保持后台执行当前DisconnectStationDevicesAsync已通过Task.Run执行设备释放且会先解绑上下文、停止 PLC 轮询再释放 Socket这部分方向是正确的应保留。建议执行顺序修改SelectMode和SetOfflineAsync移除.GetAwaiter().GetResult()。RFID 写回增加Connected判断并放入后台线程。PLC、主控板增加IsConnected判断。PLC 三条指令改为失败即停。保留离线状态更新和设备释放即使硬件写入失败也必须完成。使用 PLC、主控板、RFID 全部断电场景验证 UI 响应、日志和连接释放。