From 756dea82f84184e3b02dc98fc2ea1390e17959f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=85=86=E5=B0=89?= <228127304@qq.com> Date: Wed, 26 Aug 2026 23:03:55 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20EventCar=20=E5=90=8C=E8=BD=A6=20Tick=20?= =?UTF-8?q?=E4=B8=B2=E8=A1=8C=E5=B9=B6=E8=A6=86=E7=9B=96=E5=9F=BA=E7=B1=BB?= =?UTF-8?q?=20Stop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 上一拍未结束则跳过本拍,避免断网重试堆积;Stop 调用 base.Stop 并清空工作线程。 Co-authored-by: Cursor --- .../Scheduler/EventCarMission.cs | 54 +++++++++++++++---- .../FassEvent/FassEventCarHandler.cs | 2 +- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/StandardScene.Magnetic/Scheduler/EventCarMission.cs b/StandardScene.Magnetic/Scheduler/EventCarMission.cs index 451f462..2d92eaf 100644 --- a/StandardScene.Magnetic/Scheduler/EventCarMission.cs +++ b/StandardScene.Magnetic/Scheduler/EventCarMission.cs @@ -8,6 +8,7 @@ using SimpleCore.PropType; using StandardScene; using StandardScene.Scheduler.FassEvent; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; @@ -68,6 +69,9 @@ namespace StandardScene.Scheduler /// 记录每辆车上一周期的 CurrentNode,用于 PrevNode 触发与地标变化时清除任务防重复标记。 [JsonIgnore] private readonly Dictionary _prevNodeByCarId = new Dictionary(); + /// 每车最多一个在飞 Tick,避免断网重试把 Task.Run 堆爆、同一车并发下发。 + [JsonIgnore] private readonly ConcurrentDictionary _tickInFlight = new ConcurrentDictionary(); + /// 进程运行状态扩展字段。 public class EventCarMissionStatus : MissionStatus { @@ -140,9 +144,9 @@ namespace StandardScene.Scheduler Diagnosis.Post($"FASS EventCar 配置已加载:{path},事件数={_eventCars.Count}", "FassEventCar", true); } - /// 停止轮询并等待工作线程结束(最多 2 秒)。 + /// 停止轮询并等待工作线程结束(最多 2 秒)。覆盖基类 Stop,避免全量重载后线程仍在对全场车下发。 [MethodMember(Name = "停止进程", Description = "停止 FASS 车辆事件轮询")] - public void Stop() + public override void Stop() { _started = false; try @@ -153,8 +157,10 @@ namespace StandardScene.Scheduler { } - status.status = "已停止"; + _workerThread = null; Diagnosis.Post("FASS车辆事件进程已停止", "FassEventCar", true); + base.Stop(); + status.status = "已停止"; } /// @@ -182,14 +188,18 @@ namespace StandardScene.Scheduler } /// - /// 遍历所有 Car,构建快照并异步调用 Handler(与 FASS Task.Run 行为一致)。 - /// - /// 跳过 status 为空或 usage.refreshing=false 的车辆(未参与调度刷新的车不处理)。 - /// 当地标变化时清除 ,允许在新地标重新触发事件。 - /// + /// 遍历所有 Car,构建快照后 Task.Run 处理:一车断网不堵其它车。 + /// 同一车上一拍未结束则跳过本拍,禁止无界堆积。 /// private void ProcessAllCars() { + var handler = _handler; + var eventCars = _eventCars; + if (handler == null || eventCars == null) + { + return; + } + foreach (var car in SimpleLib.GetAllCars().OfType()) { if (car.status == null) @@ -207,13 +217,37 @@ namespace StandardScene.Scheduler _prevNodeByCarId.TryGetValue(car.id, out var prevNode); var snapshot = FassEventCarSnapshotFactory.Create(car, prevNode, FassEventCarSnapshotFactory.GetNextNodeCode(car)); - Task.Run(() => _handler.HandleCarTimerTick(car, snapshot, _eventCars)); - if (!string.Equals(prevNode, currentNode, StringComparison.OrdinalIgnoreCase)) { _prevNodeByCarId[car.id] = currentNode; ClearTaskTags(car); } + + if (!_tickInFlight.TryAdd(car.id, 0)) + { + continue; + } + + var carId = car.id; + Task.Run(() => + { + try + { + if (_started) + handler.HandleCarTimerTick(car, snapshot, eventCars); + } + catch (Exception ex) + { + Diagnosis.Post( + $"FASS车辆事件处理异常 car={carId}:{ExceptionFormatter.FormatEx(ex)}", + "FassEventCar", + true); + } + finally + { + _tickInFlight.TryRemove(carId, out _); + } + }); } } diff --git a/StandardScene.Magnetic/Scheduler/FassEvent/FassEventCarHandler.cs b/StandardScene.Magnetic/Scheduler/FassEvent/FassEventCarHandler.cs index 4786507..fce9010 100644 --- a/StandardScene.Magnetic/Scheduler/FassEvent/FassEventCarHandler.cs +++ b/StandardScene.Magnetic/Scheduler/FassEvent/FassEventCarHandler.cs @@ -43,7 +43,7 @@ namespace StandardScene.Scheduler.FassEvent } /// - /// 单次 TimerTick 对一辆车的处理入口(通常在 Task.Run 中调用)。 + /// 单次 TimerTick 对一辆车的处理入口(由 EventCarMission 按车 Task.Run,同车同时只跑一拍)。 /// 仅处理 TriggerEvent=TimerTick 的配置项。 /// public void HandleCarTimerTick(Car car, FassEventCarSnapshot snapshot, IReadOnlyList eventCars)