GeWuYou a79f02c987 docs(api): 添加 Core API 参考文档与事件系统接口文档
- 新增 Core API 参考文档,涵盖架构与模块、数据模型与系统、命令与查询等核心组件
- 添加事件系统接口详细文档,包括 IEvent、IEventBus、IUnRegister 等接口说明
- 提供完整的 API 使用示例路径、最佳实践与性能建议
- 包含架构图、依赖关系图与故障排查指南
- 添加测试用例参考与扩展方法说明
- [skip ci]
2026-01-21 23:45:10 +08:00

20 KiB
Raw Blame History

协程调度系统

**本文引用的文件** - [CoroutineScheduler.cs](file://GFramework.Core/coroutine/CoroutineScheduler.cs) - [CoroutineHandle.cs](file://GFramework.Core/coroutine/CoroutineHandle.cs) - [CoroutineSlot.cs](file://GFramework.Core/coroutine/CoroutineSlot.cs) - [CoroutineMetadata.cs](file://GFramework.Core/coroutine/CoroutineMetadata.cs) - [CoroutineHelper.cs](file://GFramework.Core/coroutine/CoroutineHelper.cs) - [Delay.cs](file://GFramework.Core/coroutine/Delay.cs) - [WaitForFrames.cs](file://GFramework.Core/coroutine/WaitForFrames.cs) - [WaitUntil.cs](file://GFramework.Core/coroutine/WaitUntil.cs) - [WaitWhile.cs](file://GFramework.Core/coroutine/WaitWhile.cs) - [WaitOneFrame.cs](file://GFramework.Core/coroutine/WaitOneFrame.cs) - [WaitForCoroutine.cs](file://GFramework.Core/coroutine/WaitForCoroutine.cs) - [CoroutineState.cs](file://GFramework.Core.Abstractions/coroutine/CoroutineState.cs) - [IYieldInstruction.cs](file://GFramework.Core.Abstractions/coroutine/IYieldInstruction.cs) - [ITimeSource.cs](file://GFramework.Core.Abstractions/coroutine/ITimeSource.cs) - [CoroutineExtensions.cs](file://GFramework.Godot/coroutine/CoroutineExtensions.cs)

目录

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能考量
  8. 故障排查指南
  9. 结论
  10. 附录

简介

本文件面向GFramework协程调度系统围绕CoroutineScheduler高性能执行引擎的设计与实现进行深入解析涵盖协程状态管理、元数据与槽位的内存复用策略、各类YieldInstruction的使用场景与性能特征、协程生命周期与取消机制并提供性能优化与最佳实践建议帮助开发者构建高效稳定的异步游戏逻辑。

项目结构

协程子系统位于GFramework.Core与GFramework.Core.Abstractions中Godot平台扩展位于GFramework.Godot。核心文件组织如下

  • 抽象层:定义协程状态、等待指令接口与时间源接口
  • 核心调度:调度器、句柄、槽位、元数据
  • 等待指令Delay、WaitForFrames、WaitUntil、WaitWhile、WaitOneFrame、WaitForCoroutine
  • 辅助工具CoroutineHelper提供常用等待指令与重复调用模式
  • 平台扩展Godot扩展提供节点生存期与协程取消的便捷方法
graph TB
subgraph "抽象层"
A["CoroutineState.cs"]
B["IYieldInstruction.cs"]
C["ITimeSource.cs"]
end
subgraph "核心调度"
D["CoroutineScheduler.cs"]
E["CoroutineHandle.cs"]
F["CoroutineSlot.cs"]
G["CoroutineMetadata.cs"]
end
subgraph "等待指令"
H["Delay.cs"]
I["WaitForFrames.cs"]
J["WaitUntil.cs"]
K["WaitWhile.cs"]
L["WaitOneFrame.cs"]
M["WaitForCoroutine.cs"]
end
subgraph "辅助工具"
N["CoroutineHelper.cs"]
end
subgraph "平台扩展"
O["CoroutineExtensions.cs"]
end
D --> F
D --> G
D --> E
D --> C
F --> B
H --> B
I --> B
J --> B
K --> B
L --> B
M --> B
N --> H
N --> I
N --> J
N --> K
N --> L
O --> D

图表来源

章节来源

核心组件

  • 协程调度器 CoroutineScheduler负责协程的启动、推进、暂停、恢复、终止、按标签清理、等待其他协程完成等维护槽位数组、元数据字典、标签集合、等待映射与活跃计数。
  • 协程句柄 CoroutineHandle轻量级唯一标识符支持实例隔离与键校验用于快速定位与比较协程。
  • 协程槽位 CoroutineSlot承载单个协程的枚举器、当前状态与等待指令。
  • 协程元数据 CoroutineMetadata记录槽位索引、状态、标签与活跃判定。
  • 等待指令 IYieldInstruction统一的等待协议各具体指令实现Update与IsDone。
  • 辅助工具 CoroutineHelper提供常用等待指令与重复调用模式的便捷封装。
  • 平台扩展 CoroutineExtensionsGodot提供RunCoroutine扩展与基于节点生存期的自动取消。

章节来源

架构总览

调度器采用“槽位+元数据”的双索引模型以数组O(1)访问协程配合字典实现标签与等待关系管理。每帧Update遍历所有槽位推进协程状态机处理等待指令与异常最终完成并唤醒等待者。

sequenceDiagram
participant T as "时间源(ITimeSource)"
participant S as "调度器(CoroutineScheduler)"
participant Sl as "槽位(CoroutineSlot)"
participant Y as "等待指令(IYieldInstruction)"
participant M as "元数据(CoroutineMetadata)"
S->>T : "Update()"
T-->>S : "DeltaTime"
loop 遍历槽位
S->>Sl : "检查状态与Waiting"
alt 正在等待
S->>Y : "Waiting.Update(DeltaTime)"
alt 未完成
S-->>S : "跳过该协程"
else 完成
S->>Sl : "Waiting = null"
end
else 无等待
S->>Sl : "MoveNext()"
alt 枚举结束
S->>S : "Complete(slotIndex)"
else 返回新指令
S->>Sl : "Waiting = Current"
end
end
end

图表来源

详细组件分析

协程调度器 CoroutineScheduler

  • 关键职责
    • 启动协程预热推进一次MoveNext立即获取首个等待指令
    • 每帧推进遍历槽位处理Waiting与MoveNext捕获异常并完成协程
    • 生命周期管理Pause/Resume/Kill/WaitForCoroutine/Clear
    • 标签管理:按标签批量终止
  • 数据结构
    • 槽位数组按索引O(1)访问协程
    • 元数据字典Handle->Meta记录槽位索引、状态、标签
    • 标签集合Tag->Handles支持按标签清理
    • 等待映射Target->Waiters完成时唤醒等待者
  • 性能特性
    • 预热Prewarm减少首帧开销
    • 扩容策略Array.Resize按倍增长
    • 异常路径直接Complete避免悬挂状态
classDiagram
class CoroutineScheduler {
-Dictionary<CoroutineHandle, CoroutineMetadata> _metadata
-Dictionary<string, HashSet<CoroutineHandle>> _tagged
-Dictionary<CoroutineHandle, HashSet<CoroutineHandle>> _waiting
-CoroutineSlot[] _slots
-int _activeCount
-int _nextSlot
+Run(coroutine, tag) CoroutineHandle
+Update() void
+Pause(handle) bool
+Resume(handle) bool
+Kill(handle) bool
+WaitForCoroutine(current, target) void
+KillByTag(tag) int
+Clear() int
-Prewarm(slotIndex) void
-Complete(slotIndex) void
-OnError(slotIndex, ex) void
-Expand() void
-AddTag(tag, handle) void
-RemoveTag(handle) void
}
class CoroutineSlot {
+Enumerator
+State
+Waiting
}
class CoroutineMetadata {
+int SlotIndex
+CoroutineState State
+string Tag
+bool IsActive
}
CoroutineScheduler --> CoroutineSlot : "持有"
CoroutineScheduler --> CoroutineMetadata : "映射"

图表来源

章节来源

协程句柄 CoroutineHandle

  • 设计要点
    • 使用低4位作为Key高4位作为实例ID支持最多16个实例槽位
    • 通过静态NextIndex数组为每个实例分配连续ID避免冲突
    • 有效性判断基于Key是否为0
  • 适用场景
    • 快速Handle比较与字典键
    • 与调度器内部索引解耦,便于跨模块传递
classDiagram
class CoroutineHandle {
-int _id
-static int[] NextIndex
+CoroutineHandle(instanceId)
+Key : byte
+IsValid : bool
+Equals(other) bool
+GetHashCode() int
}

图表来源

章节来源

协程槽位与元数据

  • CoroutineSlot
    • 保存Enumerator、State、Waiting承载单协程的执行上下文
  • CoroutineMetadata
    • 保存SlotIndex、State、Tag与IsActive判定用于调度器查询与清理
classDiagram
class CoroutineSlot {
+IEnumerator~IYieldInstruction~ Enumerator
+CoroutineState State
+IYieldInstruction Waiting
}
class CoroutineMetadata {
+int SlotIndex
+CoroutineState State
+string Tag
+bool IsActive
}
CoroutineSlot <.. CoroutineMetadata : "由调度器维护"

图表来源

章节来源

等待指令 IYieldInstruction 及其实现

  • 协议
    • Update(deltaTime):每帧由调度器调用,推进等待状态
    • IsDone指示等待是否完成
  • 具体实现
    • Delay基于剩余秒数递减适合通用延时
    • WaitForFrames基于帧计数递减适合固定帧率同步
    • WaitUntil基于谓词函数适合条件达成即继续
    • WaitWhile基于谓词取反适合条件持续满足时阻塞
    • WaitOneFrame单帧等待适合简单帧同步
    • WaitForCoroutine内部指令配合调度器等待其他协程完成
classDiagram
class IYieldInstruction {
<<interface>>
+bool IsDone
+void Update(deltaTime)
}
class Delay {
-double _remaining
+Update(deltaTime) void
+IsDone : bool
}
class WaitForFrames {
-int _remaining
+Update(deltaTime) void
+IsDone : bool
}
class WaitUntil {
-Func~bool~ _predicate
+Update(deltaTime) void
+IsDone : bool
}
class WaitWhile {
-Func~bool~ _predicate
+Update(deltaTime) void
+IsDone : bool
}
class WaitOneFrame {
-bool _done
+Update(deltaTime) void
+IsDone : bool
}
class WaitForCoroutine {
-bool _done
+Update(deltaTime) void
+IsDone : bool
+Complete() void
}
Delay ..|> IYieldInstruction
WaitForFrames ..|> IYieldInstruction
WaitUntil ..|> IYieldInstruction
WaitWhile ..|> IYieldInstruction
WaitOneFrame ..|> IYieldInstruction
WaitForCoroutine ..|> IYieldInstruction

图表来源

章节来源

协程生命周期与取消机制

  • 生命周期
    • 创建Run生成Handle并分配槽位预热推进一次
    • 执行Update推进枚举器处理Waiting与MoveNext
    • 完成枚举结束或异常触发Complete移除标签与元数据唤醒等待者
    • 清理Pause/Resume/Kill/WaitForCoroutine/Clear
  • 取消与平台集成
    • Godot扩展提供CancelWith系列方法基于节点生存期自动停止协程迭代
    • WaitForCoroutine内部指令用于显式等待其他协程完成
flowchart TD
Start(["开始: Run(coroutine, tag)"]) --> Prewarm["预热: MoveNext获取首个等待指令"]
Prewarm --> Loop{"每帧Update循环"}
Loop --> CheckWaiting{"存在Waiting?"}
CheckWaiting --> |是| UpdateWaiting["Waiting.Update(DeltaTime)"]
UpdateWaiting --> WaitingDone{"IsDone?"}
WaitingDone --> |否| NextSlot["下一槽位"]
WaitingDone --> |是| ClearWaiting["Waiting = null"]
CheckWaiting --> |否| MoveNext["MoveNext()"]
MoveNext --> Done{"枚举结束?"}
Done --> |是| Complete["Complete(slotIndex)<br/>移除标签/元数据<br/>唤醒等待者"]
Done --> |否| SetWaiting["Waiting = Current"]
ClearWaiting --> Loop
SetWaiting --> Loop
NextSlot --> Loop
Complete --> End(["结束"])

图表来源

章节来源

协程状态管理与元数据

  • 状态枚举 CoroutineState
    • Running/Paused/Held/Completed/Cancelled覆盖暂停、等待其他协程、完成与取消等场景
  • 元数据字段
    • SlotIndex用于快速定位槽位
    • State当前状态
    • Tag标签支持按标签批量管理
    • IsActive运行中/暂停/挂起视为活跃

章节来源

协程槽位的内存管理与复用

  • 槽位数组
    • 初始容量可配置,按倍增长扩容,降低频繁分配成本
    • 完成后仅清空引用,不释放数组本身,实现复用
  • 标签与等待映射
    • 使用HashSet存储Handle避免重复与频繁扩容
    • 完成时清理等待映射,防止悬挂引用

章节来源

协程辅助工具与常用模式

  • CoroutineHelper
    • WaitForSeconds/WaitForFrames/WaitUntil/WaitWhile/WaitOneFrame
    • DelayedCall/RepeatCall/RepeatCallForever组合等待指令与动作委托形成常用异步模式

章节来源

依赖关系分析

  • 抽象接口驱动实现IYieldInstruction与ITimeSource定义了调度器与等待指令的契约
  • 调度器依赖ITimeSource提供DeltaTimeCoroutineSlot与CoroutineMetadata承载执行上下文
  • 平台扩展Godot扩展通过Timing与节点生存期集成增强取消语义
graph LR
IYield["IYieldInstruction.cs"] --> Dly["Delay.cs"]
IYield --> Wff["WaitForFrames.cs"]
IYield --> Wun["WaitUntil.cs"]
IYield --> Wwh["WaitWhile.cs"]
IYield --> Wof["WaitOneFrame.cs"]
IYield --> Wfc["WaitForCoroutine.cs"]
ITime["ITimeSource.cs"] --> Sch["CoroutineScheduler.cs"]
Sch --> Sl["CoroutineSlot.cs"]
Sch --> Meta["CoroutineMetadata.cs"]
Sch --> Hdl["CoroutineHandle.cs"]
Ext["CoroutineExtensions.cs"] --> Sch

图表来源

章节来源

性能考量

  • 时间源与DeltaTime
    • 通过ITimeSource集中管理时间确保调度器与外部系统解耦
  • 槽位数组与字典
    • 数组O(1)访问字典O(1)查找,避免线性扫描
    • 扩容策略按倍增长,摊销分配成本
  • 预热与异常处理
    • 预热减少首帧等待异常路径直接Complete避免悬挂
  • 等待指令选择
    • 帧对齐需求优先WaitForFrames通用延时优先Delay条件等待优先WaitUntil/WaitWhile
  • 标签与等待映射
    • 按标签批量清理避免逐个Handle遍历
  • 平台取消
    • 基于节点生存期的CancelWith减少泄漏与无效迭代

章节来源

故障排查指南

  • 协程未推进
    • 检查是否正确调用Update确认Handle有效查看Waiting是否卡住
  • 协程无法暂停/恢复
    • 确认当前状态为Running/Paused检查状态切换逻辑
  • 协程无法按标签终止
    • 检查标签是否正确设置;确认标签集合是否存在
  • 等待指令不生效
    • 确认Update被每帧调用检查IsDone逻辑验证参数边界如WaitForFrames最小值
  • 异常导致协程悬挂
    • 查看OnError路径是否触发Complete确认异常被捕获并清理资源

章节来源

结论

GFramework协程调度系统以“槽位+元数据”为核心,结合抽象接口与平台扩展,实现了高性能、可维护、易扩展的异步执行引擎。通过合理的等待指令选择、生命周期管理与取消机制,开发者可以构建稳定高效的异步游戏逻辑。

附录

  • 最佳实践
    • 优先使用WaitForFrames进行帧对齐使用Delay进行通用延时使用WaitUntil/WaitWhile表达条件等待
    • 为长生命周期协程设置标签,便于批量管理与清理
    • 在Godot中使用CancelWith确保节点销毁时协程自动停止
    • 避免在协程中进行重型同步操作;尽量拆分为多步等待
  • 常见陷阱
    • 忘记调用Update导致协程停滞
    • 在协程中直接抛出未捕获异常造成悬挂
    • 使用WaitForCoroutine时等待自身或目标不存在
    • 标签管理不当导致内存泄漏