mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-25 21:34:28 +08:00
- 新增 Core API 参考文档,涵盖架构与模块、数据模型与系统、命令与查询等核心组件 - 添加事件系统接口详细文档,包括 IEvent、IEventBus、IUnRegister 等接口说明 - 提供完整的 API 使用示例路径、最佳实践与性能建议 - 包含架构图、依赖关系图与故障排查指南 - 添加测试用例参考与扩展方法说明 - [skip ci]
22 KiB
22 KiB
协程调度器核心
**本文引用的文件** - [CoroutineScheduler.cs](file://GFramework.Core/coroutine/CoroutineScheduler.cs) - [CoroutineHandle.cs](file://GFramework.Core/coroutine/CoroutineHandle.cs) - [CoroutineMetadata.cs](file://GFramework.Core/coroutine/CoroutineMetadata.cs) - [CoroutineSlot.cs](file://GFramework.Core/coroutine/CoroutineSlot.cs) - [WaitForCoroutine.cs](file://GFramework.Core/coroutine/WaitForCoroutine.cs) - [Delay.cs](file://GFramework.Core/coroutine/Delay.cs) - [WaitOneFrame.cs](file://GFramework.Core/coroutine/WaitOneFrame.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) - [CoroutineHelper.cs](file://GFramework.Core/coroutine/CoroutineHelper.cs) - [CoroutineState.cs](file://GFramework.Core.Abstractions/coroutine/CoroutineState.cs) - [IYieldInstruction.cs](file://GFramework.Core.Abstractions/coroutine/IYieldInstruction.cs) - [CoroutineSchedulerTests.cs](file://GFramework.Core.Tests/coroutine/CoroutineSchedulerTests.cs)目录
简介
本文件围绕 CoroutineScheduler 核心调度器进行深入技术文档化,重点解释其架构设计、高性能执行引擎实现以及关键方法的工作流程。文档覆盖以下主题:
- Run 方法的协程启动流程:槽位分配、元数据创建、预热机制
- Update 方法的协程状态更新循环:等待指令处理、协程推进、异常捕获
- 协程生命周期管理:暂停(Pause)、恢复(Resume)、终止(Kill)
- 协程等待机制:WaitForCoroutine 的依赖等待与唤醒逻辑
- 协程标签系统与批量管理
- 性能优化策略与内存管理最佳实践
- 实际使用示例与常见问题解决方案
项目结构
该调度器位于 GFramework.Core/coroutine 目录下,配合抽象层接口与若干等待指令实现共同构成协程系统。
graph TB
subgraph "协程系统"
S["CoroutineScheduler.cs"]
H["CoroutineHandle.cs"]
M["CoroutineMetadata.cs"]
L["CoroutineSlot.cs"]
WFC["WaitForCoroutine.cs"]
D["Delay.cs"]
WOF["WaitOneFrame.cs"]
WFF["WaitForFrames.cs"]
WU["WaitUntil.cs"]
WW["WaitWhile.cs"]
CH["CoroutineHelper.cs"]
end
subgraph "抽象层"
CS["CoroutineState.cs"]
YI["IYieldInstruction.cs"]
end
S --> H
S --> M
S --> L
S --> WFC
S --> D
S --> WOF
S --> WFF
S --> WU
S --> WW
S -.-> YI
S -.-> CS
CH --> D
CH --> WOF
CH --> WFF
CH --> WU
CH --> WW
图表来源
- CoroutineScheduler.cs
- CoroutineHandle.cs
- CoroutineMetadata.cs
- CoroutineSlot.cs
- WaitForCoroutine.cs
- Delay.cs
- WaitOneFrame.cs
- WaitForFrames.cs
- WaitUntil.cs
- WaitWhile.cs
- CoroutineHelper.cs
- CoroutineState.cs
- IYieldInstruction.cs
章节来源
- CoroutineScheduler.cs
- CoroutineHandle.cs
- CoroutineMetadata.cs
- CoroutineSlot.cs
- WaitForCoroutine.cs
- Delay.cs
- WaitOneFrame.cs
- WaitForFrames.cs
- WaitUntil.cs
- WaitWhile.cs
- CoroutineHelper.cs
- CoroutineState.cs
- IYieldInstruction.cs
核心组件
- 协程调度器 CoroutineScheduler:负责协程的创建、更新、暂停/恢复/终止、等待与标签管理、容量扩展与异常处理。
- 协程句柄 CoroutineHandle:唯一标识协程实例,支持实例隔离与键值提取。
- 协程槽位 CoroutineSlot:承载单个协程的枚举器、状态与等待指令。
- 协程元数据 CoroutineMetadata:记录槽位索引、状态、标签与活跃性判断。
- 等待指令 IYieldInstruction 及其实现:Delay、WaitOneFrame、WaitForFrames、WaitUntil、WaitWhile、WaitForCoroutine。
- 协程辅助工具 CoroutineHelper:提供常用等待指令的便捷构造方法。
章节来源
- CoroutineScheduler.cs
- CoroutineHandle.cs
- CoroutineSlot.cs
- CoroutineMetadata.cs
- IYieldInstruction.cs
- Delay.cs
- WaitOneFrame.cs
- WaitForFrames.cs
- WaitUntil.cs
- WaitWhile.cs
- WaitForCoroutine.cs
- CoroutineHelper.cs
架构总览
调度器采用“槽位+元数据”的双映射结构:
- 槽位数组按顺序存储协程执行上下文,便于 O(1) 遍历与更新。
- 元数据字典将句柄映射到状态、槽位索引与标签,支持快速查询与状态变更。
- 等待集合维护“被等待者”到“等待者”的反向依赖,实现唤醒链路。
- 标签集合支持按标签批量管理协程。
classDiagram
class CoroutineScheduler {
-Dictionary~CoroutineHandle, CoroutineMetadata~ _metadata
-Dictionary~string, HashSet~CoroutineHandle~~ _tagged
-Dictionary~CoroutineHandle, HashSet~CoroutineHandle~~ _waiting
-CoroutineSlot?[] _slots
-int _activeCount
-int _nextSlot
+DeltaTime : double
+ActiveCoroutineCount : int
+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 : IEnumerator~IYieldInstruction~
+State : CoroutineState
+Waiting : IYieldInstruction?
}
class CoroutineMetadata {
+SlotIndex : int
+State : CoroutineState
+Tag : string?
+IsActive : bool
}
class CoroutineHandle {
+Key : byte
+IsValid : bool
+Equals(other) bool
+GetHashCode() int
}
class IYieldInstruction {
<<interface>>
+IsDone : bool
+Update(deltaTime) void
}
CoroutineScheduler --> CoroutineSlot : "管理"
CoroutineScheduler --> CoroutineMetadata : "映射"
CoroutineScheduler --> CoroutineHandle : "键"
CoroutineSlot --> IYieldInstruction : "持有"
图表来源
详细组件分析
Run 方法:协程启动流程
- 输入校验:若协程为空,直接返回无效句柄。
- 槽位扩容:若当前槽位已满,则按倍增策略扩展数组容量。
- 分配句柄与槽位:生成新的协程句柄,并递增下一个可用槽位索引。
- 创建槽位:初始化枚举器与运行状态。
- 创建元数据:记录槽位索引、运行状态与可选标签。
- 标签注册:若提供标签,加入标签集合。
- 预热执行:立即推进一次协程,读取首个等待指令;异常将被捕获并完成协程。
- 活跃计数增加:确保统计准确。
sequenceDiagram
participant Caller as "调用方"
participant Scheduler as "CoroutineScheduler"
participant Slots as "槽位数组"
participant Meta as "元数据字典"
participant Tag as "标签集合"
Caller->>Scheduler : Run(协程, 标签?)
Scheduler->>Scheduler : 检查协程是否为空
alt 协程为空
Scheduler-->>Caller : 返回无效句柄
else 协程非空
Scheduler->>Scheduler : 检查槽位容量并扩展
Scheduler->>Scheduler : 分配新句柄与槽位索引
Scheduler->>Slots : 创建并写入槽位
Scheduler->>Meta : 创建并写入元数据
alt 提供了标签
Scheduler->>Tag : 注册标签
end
Scheduler->>Scheduler : 预热(推进一次)
Scheduler->>Scheduler : 活跃计数+1
Scheduler-->>Caller : 返回句柄
end
图表来源
章节来源
Update 方法:协程状态更新循环
- 时间源更新:先驱动时间源,获取 DeltaTime。
- 遍历槽位:仅遍历已分配的槽位(_nextSlot),跳过空槽位与非运行状态。
- 等待指令处理:若存在等待指令,调用 Update(delta),若未完成则跳过本次推进。
- 协程推进:MoveNext(),若返回 false 则完成协程;否则将当前指令作为等待指令。
- 异常捕获:任何异常均转交 OnError,统一完成协程并输出错误日志。
flowchart TD
Start(["进入 Update"]) --> UpdateTime["更新时间源<br/>获取 DeltaTime"]
UpdateTime --> Loop["遍历槽位 i=0.._nextSlot-1"]
Loop --> CheckSlot{"槽位有效且运行中?"}
CheckSlot --> |否| NextIter["继续下一个槽位"]
CheckSlot --> |是| WaitCheck{"存在等待指令?"}
WaitCheck --> |是| UpdateWait["调用 Waiting.Update(delta)"]
UpdateWait --> WaitDone{"IsDone==true?"}
WaitDone --> |否| NextIter
WaitDone --> |是| ClearWait["清空等待指令"]
WaitCheck --> |否| MoveNext["MoveNext()"]
ClearWait --> MoveNext
MoveNext --> Done{"MoveNext 返回?"}
Done --> |否| Complete["Complete(slotIndex)"]
Done --> |是| SetWait["slot.Waiting = 当前指令"]
Complete --> NextIter
SetWait --> NextIter
NextIter --> Loop
Loop --> End(["结束"])
图表来源
章节来源
生命周期管理:Pause/Resume/Kill
- Pause:仅对运行中的协程生效,将其与元数据同时置为暂停。
- Resume:仅对暂停中的协程生效,恢复为运行。
- Kill:直接完成指定槽位的协程,触发标签移除与等待者唤醒。
sequenceDiagram
participant Caller as "调用方"
participant Scheduler as "CoroutineScheduler"
participant Slot as "CoroutineSlot"
participant Meta as "CoroutineMetadata"
Caller->>Scheduler : Pause(handle)
Scheduler->>Meta : 查找元数据
alt 元数据存在且槽位有效且运行中
Scheduler->>Slot : 设置为 Paused
Scheduler->>Meta : 设置为 Paused
Scheduler-->>Caller : 返回 true
else 否
Scheduler-->>Caller : 返回 false
end
Caller->>Scheduler : Resume(handle)
Scheduler->>Meta : 查找元数据
alt 元数据存在且槽位有效且暂停中
Scheduler->>Slot : 设置为 Running
Scheduler->>Meta : 设置为 Running
Scheduler-->>Caller : 返回 true
else 否
Scheduler-->>Caller : 返回 false
end
Caller->>Scheduler : Kill(handle)
Scheduler->>Scheduler : Complete(slotIndex)
Scheduler-->>Caller : 返回 true
图表来源
章节来源
等待机制:WaitForCoroutine 依赖等待与唤醒
- 自等待检查:禁止协程等待自身。
- 目标存在性:若目标不存在,直接返回(不阻塞当前协程)。
- 当前协程状态:设置为 Held,防止被 Update 推进。
- 等待登记:将当前协程加入“被等待者”的等待集合。
- 唤醒逻辑:目标协程完成后,遍历等待者集合,逐个恢复为 Running 并清理等待登记。
sequenceDiagram
participant Caller as "调用方"
participant Scheduler as "CoroutineScheduler"
participant Curr as "当前协程槽位"
participant Target as "目标协程槽位"
participant WaitSet as "等待集合"
Caller->>Scheduler : WaitForCoroutine(current, target)
Scheduler->>Scheduler : 检查是否等待自身
alt 等待自身
Scheduler-->>Caller : 抛出异常
else 非自身
Scheduler->>Scheduler : 检查目标是否存在
opt 目标存在
Scheduler->>Curr : 设置为 Held
Scheduler->>WaitSet : 将 current 加入 target 的等待集合
end
end
Note over Target,Scheduler : 目标协程完成后
Target->>Scheduler : Complete(slotIndex)
Scheduler->>WaitSet : 获取等待者集合
loop 对每个等待者
Scheduler->>Curr : 设置为 Running
end
Scheduler->>WaitSet : 清理等待集合
图表来源
章节来源
标签系统与批量管理
- 标签注册:Run 时若提供标签,加入标签集合并同步到元数据。
- 批量终止:KillByTag 遍历标签集合中的句柄,逐一调用 Kill。
- 清空:Clear 将所有集合清空并重置计数与索引。
flowchart TD
AddTag["AddTag(tag, handle)"] --> Lookup{"标签存在?"}
Lookup --> |否| NewSet["新建集合并加入 handle"]
Lookup --> |是| Append["追加 handle 到集合"]
NewSet --> MetaTag["元数据写入 Tag"]
Append --> MetaTag
KillByTag["KillByTag(tag)"] --> Exists{"标签存在?"}
Exists --> |否| ReturnZero["返回 0"]
Exists --> |是| Copy["复制句柄集合"]
Copy --> Loop["遍历句柄并 Kill"]
Loop --> Count["累计计数"]
Count --> ReturnCount["返回计数"]
Clear["Clear()"] --> Reset["清空集合/数组/计数/索引"]
图表来源
章节来源
等待指令族与使用建议
- Delay:基于剩余时间递减,适合秒级延迟。
- WaitOneFrame:每帧完成一次,适合简单帧等待。
- WaitForFrames:基于帧计数,适合固定帧数等待。
- WaitUntil/WaitWhile:基于谓词函数,适合条件等待。
- WaitForCoroutine:用于协程间依赖等待,需配合调度器的等待登记与唤醒。
章节来源
- Delay.cs
- WaitOneFrame.cs
- WaitForFrames.cs
- WaitUntil.cs
- WaitWhile.cs
- WaitForCoroutine.cs
- CoroutineHelper.cs
依赖关系分析
- 调度器依赖抽象接口 IYieldInstruction 与枚举 CoroutineState,保证跨平台与可替换性。
- 等待指令实现遵循统一接口,调度器通过 Update 驱动其状态变化。
- 句柄与元数据形成强关联,槽位数组提供 O(1) 访问,元数据字典提供 O(1) 查询。
- 标签集合与等待集合均为哈希结构,支持高效插入、删除与遍历。
graph LR
YI["IYieldInstruction"] --> D["Delay"]
YI --> WOF["WaitOneFrame"]
YI --> WFF["WaitForFrames"]
YI --> WU["WaitUntil"]
YI --> WW["WaitWhile"]
YI --> WFC["WaitForCoroutine"]
CS["CoroutineState"] --> S["CoroutineScheduler"]
YI --> S
S --> L["CoroutineSlot"]
S --> M["CoroutineMetadata"]
S --> H["CoroutineHandle"]
图表来源
- IYieldInstruction.cs
- CoroutineState.cs
- CoroutineScheduler.cs
- Delay.cs
- WaitOneFrame.cs
- WaitForFrames.cs
- WaitUntil.cs
- WaitWhile.cs
- WaitForCoroutine.cs
章节来源
性能考量
- 时间源驱动:Update 先驱动时间源,确保等待指令的 Update 使用一致的 DeltaTime。
- 线性遍历:Update 对已分配槽位线性扫描,复杂度 O(N);N 为已分配槽位数。
- 等待指令短路:若等待未完成,跳过 MoveNext,避免无意义推进。
- 预热机制:Run 时预热一次,减少首帧推进成本。
- 容量扩展:按倍增策略扩容,摊销写入成本,降低频繁扩容开销。
- 异常处理:统一捕获并完成协程,避免异常传播影响调度器稳定性。
- 标签与等待集合:使用 HashSet,插入/删除/查找平均 O(1),适合高频操作。
章节来源
故障排查指南
- 协程未推进:确认协程枚举器返回的等待指令是否正确,且 IsDone 在条件满足时为 true。
- 协程卡住:检查等待指令 Update 是否被调用,以及剩余时间/帧数是否递减。
- WaitForCoroutine 无效:确认目标协程句柄有效,且当前协程被设置为 Held。
- 标签批量终止无效:确认标签名一致,且句柄仍在调度器中。
- 异常导致协程提前结束:查看控制台错误输出,定位异常点并修复协程逻辑。
- 槽位不足:观察活跃协程数量增长趋势,必要时增大 initialCapacity 或优化协程生命周期。
章节来源
结论
CoroutineScheduler 通过“槽位+元数据”的紧凑结构实现了高性能的协程调度,结合统一的等待指令接口与完善的生命周期管理,既保证了易用性也兼顾了性能与可维护性。配合标签系统与批量管理能力,能够满足复杂场景下的协程编排需求。
附录
使用示例与最佳实践
- 基础使用:通过 Run 启动协程,传入等待指令实现延迟、帧等待或条件等待。
- 条件等待:使用 WaitUntil/WaitWhile 根据业务状态动态决定协程推进时机。
- 协程间依赖:使用 WaitForCoroutine 建立依赖关系,目标完成后自动唤醒等待者。
- 标签管理:为协程打标签,便于批量终止与统计。
- 性能优化:合理设置 initialCapacity,避免频繁扩容;尽量使用轻量等待指令;及时 Kill 长生命周期协程。
章节来源