From d7ddecb2974be6a8b1b45718947bbb487a3751ea Mon Sep 17 00:00:00 2001 From: Blank Date: Sun, 1 Nov 2026 20:47:45 +0800 Subject: [PATCH] =?UTF-8?q?fix(core):=20=E7=BB=84=E4=BB=B6=E6=BF=80?= =?UTF-8?q?=E6=B4=BB=E5=BF=AB=E9=80=9F=E8=B7=AF=E5=BE=84=E6=94=B9=E5=88=A4?= =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=A0=87=E8=AE=B0=E5=B9=B6=E5=9C=A8=20actor?= =?UTF-8?q?=20=E4=B8=8A=E4=B8=8B=E6=96=87=E5=A4=8D=E6=A3=80=EF=BC=88?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20#673=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- GameFrameX.Core/Actors/Actor.cs | 26 +++++++++++++++++++-- GameFrameX.Core/Components/BaseComponent.cs | 22 +++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/GameFrameX.Core/Actors/Actor.cs b/GameFrameX.Core/Actors/Actor.cs index 152a8eee5..2ccd3aa0b 100644 --- a/GameFrameX.Core/Actors/Actor.cs +++ b/GameFrameX.Core/Actors/Actor.cs @@ -167,14 +167,34 @@ public async Task GetComponentAgentAsync(Type agentType, bool i } var agent = component.GetAgent(agentType); - if (!component.IsActive) + // 快速路径:只有整个激活流程结束才放行——IsActive 在激活一开始即置位, + // 激活进行中(读库/代理三段回调未完)时外部调用者必须排队等待,不能提前拿到 agent。 + if (!component.IsActivationCompleted) { - await SendAsyncWithoutCheck(() => ActivateComponentAsync(component, agent, compType)); + await SendAsyncWithoutCheck(() => ActivateComponentIfNeededAsync(component, agent, compType)); } return agent; } + /// + /// 在 actor 上下文内复检后再激活:排在前面的激活工作单元已完成(或当前是激活过程中的内联嵌套调用, + /// IsActive 已为 true)时直接返回,不重复执行激活逻辑。 + /// + /// 要激活的组件实例 + /// 组件对应的代理实例 + /// 组件类型,用于日志记录 + /// 表示异步操作的任务 + private Task ActivateComponentIfNeededAsync(BaseComponent component, IComponentAgent agent, Type compType) + { + if (component.IsActive) + { + return Task.CompletedTask; + } + + return ActivateComponentAsync(component, agent, compType); + } + /// /// 激活指定组件并依次调用其代理的激活生命周期方法 /// @@ -213,6 +233,8 @@ private async Task ActivateComponentAsync(BaseComponent component, IComponentAge throw; } } + // 与既有行为一致:激活失败也视为流程结束(不重试),等待方不再被阻塞。 + component.IsActivationCompleted = true; } /// diff --git a/GameFrameX.Core/Components/BaseComponent.cs b/GameFrameX.Core/Components/BaseComponent.cs index efb000a72..31c9c156d 100644 --- a/GameFrameX.Core/Components/BaseComponent.cs +++ b/GameFrameX.Core/Components/BaseComponent.cs @@ -54,6 +54,27 @@ internal long ActorId /// public bool IsActive { get; private set; } + /// + /// 激活流程(组件激活 + 代理三段激活回调)是否已全部完成。 + /// + /// + /// 由 actor 上下文写入,可能被 actor 外部线程读取,因此使用 volatile; + /// 与 (激活开始即置位)区分,供快速路径判断是否可以放行。 + /// Whether the whole activation flow (component activation plus the agent's three-phase + /// activation callbacks) has completed; written on the actor context and read from outside, + /// hence volatile. Distinct from (set at activation start). + /// + private volatile bool _isActivationCompleted; + + /// + /// 激活流程是否已全部完成 + /// + internal bool IsActivationCompleted + { + get { return _isActivationCompleted; } + set { _isActivationCompleted = value; } + } + /// /// 是否准备完毕 /// @@ -136,6 +157,7 @@ public virtual async Task InactiveAsync() finally { IsActive = false; + _isActivationCompleted = false; } }