Docs/coroutine system refactor#99
Conversation
- 重构协程系统架构说明,明确核心组件和入口点 - 更新协程启动方式和基本用法示例代码 - 添加 Segment 分段调度机制详细说明 - 补充节点生命周期绑定和延迟调用功能介绍 - 完善常用等待指令和协程控制方法文档 - 优化事件总线集成和上下文感知集成说明
- 重新描述协程系统功能,强调基于 IEnumerator<IYieldInstruction> 的调度能力 - 更新核心概念介绍,明确协程系统的组成部分和架构设计 - 修改 CoroutineScheduler 使用示例,添加统计信息启用说明 - 完善 CoroutineHandle 的控制方法说明,包括暂停、恢复和终止操作 - 优化协程状态控制示例代码,展示标签和分组管理功能 - 重构等待指令说明,按时间帧、条件等待、Task桥接等方式分类 - 更新事件等待相关示例,改进超时处理和多事件等待功能 - 完善协程组合功能说明,包括子协程等待和多句柄等待 - 优化扩展方法介绍,按组合扩展、协程生成扩展、Task扩展等分类 - 更新命令、查询和Mediator扩展使用说明 - 修订异常处理机制说明,明确调度器异常处理方式 - 更新常见问题解答,澄清协程执行时机和线程模型概念 - 精简相关文档链接,移除冗余的系统集成说明
审阅者指南重构并大幅扩展了 Core 和 Godot 集成部分的协程文档,使术语与新的基于 在 Godot 中使用 Segment 和 CancelWith 节点绑定运行协程的时序图sequenceDiagram
participant MyNode
participant Coroutine as DemoCoroutine
participant Timing
participant Scheduler as CoroutineScheduler
participant Node as GodotNode
MyNode->>MyNode: _Ready()
MyNode->>Coroutine: Demo()
activate Coroutine
Coroutine-->>MyNode: IEnumerator_IYieldInstruction_
MyNode->>Coroutine: CancelWith(Node)
Coroutine-->>MyNode: wrapped_IEnumerator
MyNode->>Timing: RunCoroutine(wrapped_IEnumerator, Segment.Process, tag)
activate Timing
Timing->>Scheduler: Run(wrapped_IEnumerator, tag, group)
activate Scheduler
Scheduler-->>Timing: CoroutineHandle
Timing-->>MyNode: CoroutineHandle
deactivate Timing
loop each_frame_in_Process
MyNode->>Timing: internal_Process_callback
Timing->>Scheduler: Update()
Scheduler->>Coroutine: MoveNext(deltaTime)
alt coroutine_yields_Delay
Coroutine-->>Scheduler: Delay_instruction
Scheduler-->>Scheduler: schedule_wait_seconds
else coroutine_completes
Coroutine-->>Scheduler: completed
Scheduler-->>Scheduler: release_handle
end
end
par Node_exits_scene_tree
Node-->>Coroutine: cancellation_signal
Coroutine-->>Scheduler: stop_iteration
Scheduler-->>Scheduler: Kill(CoroutineHandle)
end
deactivate Scheduler
deactivate Coroutine
使用 IContextAware 和 WaitForEventWithTimeout 的命令 + 事件协程时序图sequenceDiagram
participant Controller as GodotNode_Controller
participant Context as IContextAware
participant Scheduler as CoroutineScheduler
participant EventBus as IEventBus
participant Command as LoadSceneCommand
participant WaitInstr as WaitForEventWithTimeout_SceneLoadedEvent_
participant Coroutine as CommandCoroutine
Controller->>Context: SendCommandAndWaitEventCoroutine(Command, handler, timeout)
activate Context
Context-->>Coroutine: IEnumerator_IYieldInstruction_
deactivate Context
Controller->>Scheduler: Run(Coroutine, tag, group)
activate Scheduler
Scheduler-->>Controller: CoroutineHandle
note over Coroutine,Scheduler: 协程体开始执行
Coroutine->>Context: SendCommand(Command)
Context->>Command: Execute()
Command-->>Context: Task_completion
Coroutine->>EventBus: create_WaitForEvent_SceneLoadedEvent_
activate WaitInstr
Coroutine->>Scheduler: yield WaitForEventWithTimeout_SceneLoadedEvent_(WaitForEvent, timeoutSeconds)
loop scheduler_updates
Scheduler->>WaitInstr: poll_status(deltaTime)
alt event_published_before_timeout
EventBus-->>WaitInstr: SceneLoadedEvent
WaitInstr-->>Coroutine: IsTimeout=false, EventData
Coroutine-->>Scheduler: resume_execution
Coroutine->>Controller: handler(EventData)
Coroutine-->>Scheduler: complete
Scheduler-->>Scheduler: release_handle
note over Scheduler: break
else timeout_elapsed
WaitInstr-->>Coroutine: IsTimeout=true
Coroutine-->>Scheduler: resume_execution
Coroutine->>Controller: handler_timeout_branch
Coroutine-->>Scheduler: complete
Scheduler-->>Scheduler: release_handle
note over Scheduler: break
end
end
deactivate WaitInstr
deactivate Scheduler
更新后的核心协程系统类图classDiagram
direction LR
class CoroutineScheduler {
+CoroutineScheduler(ITimeSource timeSource, bool enableStatistics)
+CoroutineHandle Run(IEnumerator_IYieldInstruction_ routine, string tag, string group)
+bool IsCoroutineAlive(CoroutineHandle handle)
+void Pause(CoroutineHandle handle)
+void Resume(CoroutineHandle handle)
+void Kill(CoroutineHandle handle)
+void KillByTag(string tag)
+void PauseGroup(string group)
+void ResumeGroup(string group)
+void KillGroup(string group)
+bool Clear()
+void Update()
+event OnCoroutineException
}
class CoroutineHandle {
+int Id
+string Tag
+string Group
}
class IYieldInstruction {
<<interface>>
+bool MoveNext(double deltaTime)
}
class Delay {
+double Seconds
}
class WaitForSecondsRealtime {
+double Seconds
}
class WaitOneFrame {
}
class WaitForNextFrame {
}
class WaitForFrames {
+int FrameCount
}
class WaitForEndOfFrame {
}
class WaitUntil {
+Func_bool_ Predicate
}
class WaitWhile {
+Func_bool_ Predicate
}
class WaitForEvent_TEvent_ {
+TEvent EventData
+bool IsCompleted
+Dispose()
}
class WaitForEventWithTimeout_TEvent_ {
+TEvent EventData
+bool IsTimeout
}
class WaitForMultipleEvents_TFirst_TSecond_ {
+int TriggeredBy
+TFirst FirstEventData
+TSecond SecondEventData
}
class WaitForCoroutine {
+IEnumerator_IYieldInstruction_ Routine
}
class WaitForAllCoroutines {
+IReadOnlyList_CoroutineHandle_ Handles
}
class CoroutineHelper {
<<static>>
+IYieldInstruction WaitForSeconds(double seconds)
+IYieldInstruction WaitForOneFrame()
+IYieldInstruction WaitForFrames(int frames)
+IYieldInstruction WaitUntil(Func_bool_ predicate)
+IYieldInstruction WaitWhile(Func_bool_ predicate)
+IEnumerator_IYieldInstruction_ DelayedCall(double seconds, Action action)
+IEnumerator_IYieldInstruction_ RepeatCall(double interval, int count, Action action)
+IEnumerator_IYieldInstruction_ RepeatCallForever(double interval, Action action, CancellationToken token)
+IEnumerator_IYieldInstruction_ WaitForProgress(double duration, Action_double_ onProgress)
}
class CoroutineExtensions {
<<static>>
+IEnumerator_IYieldInstruction_ ExecuteAfter(double seconds, Action action)
+IEnumerator_IYieldInstruction_ RepeatEvery(double interval, Action action, int count)
+IEnumerator_IYieldInstruction_ WaitForSecondsWithProgress(double seconds, Action_double_ onProgress)
+IEnumerator_IYieldInstruction_ Sequence(IEnumerator_IYieldInstruction_ first, IEnumerator_IYieldInstruction_ second, IEnumerator_IYieldInstruction_ third)
}
class CoroutineComposeExtensions {
<<static>>
+IEnumerator_IYieldInstruction_ Then(IEnumerator_IYieldInstruction_ first, Action continuation)
+IEnumerator_IYieldInstruction_ Then(IEnumerator_IYieldInstruction_ first, IEnumerator_IYieldInstruction_ next)
}
class TaskCoroutineExtensions {
<<static>>
+IYieldInstruction AsCoroutineInstruction(Task task)
+IYieldInstruction AsCoroutineInstruction_T_(Task_T_ task)
+IEnumerator_IYieldInstruction_ ToCoroutineEnumerator(Task task)
+IEnumerator_IYieldInstruction_ ToCoroutineEnumerator_T_(Task_T_ task)
+CoroutineHandle StartTaskAsCoroutine(CoroutineScheduler scheduler, Task task)
+CoroutineHandle StartTaskAsCoroutine_T_(CoroutineScheduler scheduler, Task_T_ task)
}
class IEventBus {
<<interface>>
}
class IContextAware {
<<interface>>
}
class Mediator_IMediator_ {
}
CoroutineScheduler --> CoroutineHandle : creates
CoroutineScheduler ..> IYieldInstruction : schedules
CoroutineScheduler ..> IEventBus : used_by_instructions
IYieldInstruction <|.. Delay
IYieldInstruction <|.. WaitForSecondsRealtime
IYieldInstruction <|.. WaitOneFrame
IYieldInstruction <|.. WaitForNextFrame
IYieldInstruction <|.. WaitForFrames
IYieldInstruction <|.. WaitForEndOfFrame
IYieldInstruction <|.. WaitUntil
IYieldInstruction <|.. WaitWhile
IYieldInstruction <|.. WaitForEvent_TEvent_
IYieldInstruction <|.. WaitForEventWithTimeout_TEvent_
IYieldInstruction <|.. WaitForMultipleEvents_TFirst_TSecond_
IYieldInstruction <|.. WaitForCoroutine
IYieldInstruction <|.. WaitForAllCoroutines
WaitForEvent_TEvent_ --> IEventBus : subscribes
WaitForEventWithTimeout_TEvent_ --> WaitForEvent_TEvent_ : wraps
WaitForMultipleEvents_TFirst_TSecond_ --> IEventBus : subscribes
CoroutineHelper ..> IYieldInstruction : constructs
CoroutineHelper ..> IEnumerator_IYieldInstruction_ : generators
CoroutineExtensions ..> IEnumerator_IYieldInstruction_ : composition
CoroutineComposeExtensions ..> IEnumerator_IYieldInstruction_ : fluent_sequence
TaskCoroutineExtensions ..> IYieldInstruction : wraps_Task
TaskCoroutineExtensions ..> IEnumerator_IYieldInstruction_ : converts_Task
TaskCoroutineExtensions ..> CoroutineScheduler : starts_Task_coroutines
IContextAware ..> CoroutineScheduler : has_access_to
IContextAware ..> IEventBus : has_access_to
IContextAware ..> Mediator_IMediator_ : optional
文件级变更
提示与命令与 Sourcery 交互
自定义你的使用体验访问你的 控制面板 以:
获取帮助Original review guide in EnglishReviewer's GuideRefactors and significantly expands the coroutine documentation for both the Core and Godot integrations, aligning terminology with the new IEnumerator-based system, documenting CoroutineScheduler/CoroutineHandle/Instructions/Extensions, segment-based scheduling, node lifecycle binding, and command/query/event/Mediator integration, while pruning outdated patterns and best-practice sections. Sequence diagram for running a Godot coroutine with Segment and CancelWith node bindingsequenceDiagram
participant MyNode
participant Coroutine as DemoCoroutine
participant Timing
participant Scheduler as CoroutineScheduler
participant Node as GodotNode
MyNode->>MyNode: _Ready()
MyNode->>Coroutine: Demo()
activate Coroutine
Coroutine-->>MyNode: IEnumerator_IYieldInstruction_
MyNode->>Coroutine: CancelWith(Node)
Coroutine-->>MyNode: wrapped_IEnumerator
MyNode->>Timing: RunCoroutine(wrapped_IEnumerator, Segment.Process, tag)
activate Timing
Timing->>Scheduler: Run(wrapped_IEnumerator, tag, group)
activate Scheduler
Scheduler-->>Timing: CoroutineHandle
Timing-->>MyNode: CoroutineHandle
deactivate Timing
loop each_frame_in_Process
MyNode->>Timing: internal_Process_callback
Timing->>Scheduler: Update()
Scheduler->>Coroutine: MoveNext(deltaTime)
alt coroutine_yields_Delay
Coroutine-->>Scheduler: Delay_instruction
Scheduler-->>Scheduler: schedule_wait_seconds
else coroutine_completes
Coroutine-->>Scheduler: completed
Scheduler-->>Scheduler: release_handle
end
end
par Node_exits_scene_tree
Node-->>Coroutine: cancellation_signal
Coroutine-->>Scheduler: stop_iteration
Scheduler-->>Scheduler: Kill(CoroutineHandle)
end
deactivate Scheduler
deactivate Coroutine
Sequence diagram for command and event-based coroutine using IContextAware and WaitForEventWithTimeoutsequenceDiagram
participant Controller as GodotNode_Controller
participant Context as IContextAware
participant Scheduler as CoroutineScheduler
participant EventBus as IEventBus
participant Command as LoadSceneCommand
participant WaitInstr as WaitForEventWithTimeout_SceneLoadedEvent_
participant Coroutine as CommandCoroutine
Controller->>Context: SendCommandAndWaitEventCoroutine(Command, handler, timeout)
activate Context
Context-->>Coroutine: IEnumerator_IYieldInstruction_
deactivate Context
Controller->>Scheduler: Run(Coroutine, tag, group)
activate Scheduler
Scheduler-->>Controller: CoroutineHandle
note over Coroutine,Scheduler: Coroutine body starts
Coroutine->>Context: SendCommand(Command)
Context->>Command: Execute()
Command-->>Context: Task_completion
Coroutine->>EventBus: create_WaitForEvent_SceneLoadedEvent_
activate WaitInstr
Coroutine->>Scheduler: yield WaitForEventWithTimeout_SceneLoadedEvent_(WaitForEvent, timeoutSeconds)
loop scheduler_updates
Scheduler->>WaitInstr: poll_status(deltaTime)
alt event_published_before_timeout
EventBus-->>WaitInstr: SceneLoadedEvent
WaitInstr-->>Coroutine: IsTimeout=false, EventData
Coroutine-->>Scheduler: resume_execution
Coroutine->>Controller: handler(EventData)
Coroutine-->>Scheduler: complete
Scheduler-->>Scheduler: release_handle
note over Scheduler: break
else timeout_elapsed
WaitInstr-->>Coroutine: IsTimeout=true
Coroutine-->>Scheduler: resume_execution
Coroutine->>Controller: handler_timeout_branch
Coroutine-->>Scheduler: complete
Scheduler-->>Scheduler: release_handle
note over Scheduler: break
end
end
deactivate WaitInstr
deactivate Scheduler
Class diagram for the updated core coroutine systemclassDiagram
direction LR
class CoroutineScheduler {
+CoroutineScheduler(ITimeSource timeSource, bool enableStatistics)
+CoroutineHandle Run(IEnumerator_IYieldInstruction_ routine, string tag, string group)
+bool IsCoroutineAlive(CoroutineHandle handle)
+void Pause(CoroutineHandle handle)
+void Resume(CoroutineHandle handle)
+void Kill(CoroutineHandle handle)
+void KillByTag(string tag)
+void PauseGroup(string group)
+void ResumeGroup(string group)
+void KillGroup(string group)
+bool Clear()
+void Update()
+event OnCoroutineException
}
class CoroutineHandle {
+int Id
+string Tag
+string Group
}
class IYieldInstruction {
<<interface>>
+bool MoveNext(double deltaTime)
}
class Delay {
+double Seconds
}
class WaitForSecondsRealtime {
+double Seconds
}
class WaitOneFrame {
}
class WaitForNextFrame {
}
class WaitForFrames {
+int FrameCount
}
class WaitForEndOfFrame {
}
class WaitUntil {
+Func_bool_ Predicate
}
class WaitWhile {
+Func_bool_ Predicate
}
class WaitForEvent_TEvent_ {
+TEvent EventData
+bool IsCompleted
+Dispose()
}
class WaitForEventWithTimeout_TEvent_ {
+TEvent EventData
+bool IsTimeout
}
class WaitForMultipleEvents_TFirst_TSecond_ {
+int TriggeredBy
+TFirst FirstEventData
+TSecond SecondEventData
}
class WaitForCoroutine {
+IEnumerator_IYieldInstruction_ Routine
}
class WaitForAllCoroutines {
+IReadOnlyList_CoroutineHandle_ Handles
}
class CoroutineHelper {
<<static>>
+IYieldInstruction WaitForSeconds(double seconds)
+IYieldInstruction WaitForOneFrame()
+IYieldInstruction WaitForFrames(int frames)
+IYieldInstruction WaitUntil(Func_bool_ predicate)
+IYieldInstruction WaitWhile(Func_bool_ predicate)
+IEnumerator_IYieldInstruction_ DelayedCall(double seconds, Action action)
+IEnumerator_IYieldInstruction_ RepeatCall(double interval, int count, Action action)
+IEnumerator_IYieldInstruction_ RepeatCallForever(double interval, Action action, CancellationToken token)
+IEnumerator_IYieldInstruction_ WaitForProgress(double duration, Action_double_ onProgress)
}
class CoroutineExtensions {
<<static>>
+IEnumerator_IYieldInstruction_ ExecuteAfter(double seconds, Action action)
+IEnumerator_IYieldInstruction_ RepeatEvery(double interval, Action action, int count)
+IEnumerator_IYieldInstruction_ WaitForSecondsWithProgress(double seconds, Action_double_ onProgress)
+IEnumerator_IYieldInstruction_ Sequence(IEnumerator_IYieldInstruction_ first, IEnumerator_IYieldInstruction_ second, IEnumerator_IYieldInstruction_ third)
}
class CoroutineComposeExtensions {
<<static>>
+IEnumerator_IYieldInstruction_ Then(IEnumerator_IYieldInstruction_ first, Action continuation)
+IEnumerator_IYieldInstruction_ Then(IEnumerator_IYieldInstruction_ first, IEnumerator_IYieldInstruction_ next)
}
class TaskCoroutineExtensions {
<<static>>
+IYieldInstruction AsCoroutineInstruction(Task task)
+IYieldInstruction AsCoroutineInstruction_T_(Task_T_ task)
+IEnumerator_IYieldInstruction_ ToCoroutineEnumerator(Task task)
+IEnumerator_IYieldInstruction_ ToCoroutineEnumerator_T_(Task_T_ task)
+CoroutineHandle StartTaskAsCoroutine(CoroutineScheduler scheduler, Task task)
+CoroutineHandle StartTaskAsCoroutine_T_(CoroutineScheduler scheduler, Task_T_ task)
}
class IEventBus {
<<interface>>
}
class IContextAware {
<<interface>>
}
class Mediator_IMediator_ {
}
CoroutineScheduler --> CoroutineHandle : creates
CoroutineScheduler ..> IYieldInstruction : schedules
CoroutineScheduler ..> IEventBus : used_by_instructions
IYieldInstruction <|.. Delay
IYieldInstruction <|.. WaitForSecondsRealtime
IYieldInstruction <|.. WaitOneFrame
IYieldInstruction <|.. WaitForNextFrame
IYieldInstruction <|.. WaitForFrames
IYieldInstruction <|.. WaitForEndOfFrame
IYieldInstruction <|.. WaitUntil
IYieldInstruction <|.. WaitWhile
IYieldInstruction <|.. WaitForEvent_TEvent_
IYieldInstruction <|.. WaitForEventWithTimeout_TEvent_
IYieldInstruction <|.. WaitForMultipleEvents_TFirst_TSecond_
IYieldInstruction <|.. WaitForCoroutine
IYieldInstruction <|.. WaitForAllCoroutines
WaitForEvent_TEvent_ --> IEventBus : subscribes
WaitForEventWithTimeout_TEvent_ --> WaitForEvent_TEvent_ : wraps
WaitForMultipleEvents_TFirst_TSecond_ --> IEventBus : subscribes
CoroutineHelper ..> IYieldInstruction : constructs
CoroutineHelper ..> IEnumerator_IYieldInstruction_ : generators
CoroutineExtensions ..> IEnumerator_IYieldInstruction_ : composition
CoroutineComposeExtensions ..> IEnumerator_IYieldInstruction_ : fluent_sequence
TaskCoroutineExtensions ..> IYieldInstruction : wraps_Task
TaskCoroutineExtensions ..> IEnumerator_IYieldInstruction_ : converts_Task
TaskCoroutineExtensions ..> CoroutineScheduler : starts_Task_coroutines
IContextAware ..> CoroutineScheduler : has_access_to
IContextAware ..> IEventBus : has_access_to
IContextAware ..> Mediator_IMediator_ : optional
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
|
Overall Grade |
Security Reliability Complexity Hygiene |
Code Review Summary
| Analyzer | Status | Updated (UTC) | Details |
|---|---|---|---|
| C# | Mar 12, 2026 4:49a.m. | Review ↗ | |
| Secrets | Mar 12, 2026 4:49a.m. | Review ↗ |
There was a problem hiding this comment.
Hey - 我在这里给出了一些总体反馈:
- 在 Godot 部分关于
CancelWith(...)的说明中,建议更明确地界定节点何时被视为“失效”(例如:退出场景树 vs 被释放),这样用户就能清楚包装后的协程究竟会在什么时候停止。 - 在 Task 桥接的示例中,现在同时展示了
AsCoroutineInstruction和StartTaskAsCoroutine/ToCoroutineEnumerator;如果能简单说明在什么情况下应优先选择哪一种模式(例如:嵌入到现有协程中 vs 启动一个顶层协程),会对读者更有帮助。
给 AI Agent 的提示词
Please address the comments from this code review:
## Overall Comments
- In the Godot section on `CancelWith(...)`, consider clarifying exactly what counts as a node becoming "失效" (e.g., exiting the scene tree vs. being freed) so users know when the wrapped coroutine will actually stop.
- In the Task-bridging examples, you now show both `AsCoroutineInstruction` and `StartTaskAsCoroutine`/`ToCoroutineEnumerator`; it may help readers if you briefly spell out when to prefer each pattern (e.g., embedding inside an existing coroutine vs. starting a top-level coroutine).帮我变得更有用!请在每条评论上点 👍 或 👎,我会根据你的反馈改进后续的代码评审。
Original comment in English
Hey - I've left some high level feedback:
- In the Godot section on
CancelWith(...), consider clarifying exactly what counts as a node becoming "失效" (e.g., exiting the scene tree vs. being freed) so users know when the wrapped coroutine will actually stop. - In the Task-bridging examples, you now show both
AsCoroutineInstructionandStartTaskAsCoroutine/ToCoroutineEnumerator; it may help readers if you briefly spell out when to prefer each pattern (e.g., embedding inside an existing coroutine vs. starting a top-level coroutine).
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In the Godot section on `CancelWith(...)`, consider clarifying exactly what counts as a node becoming "失效" (e.g., exiting the scene tree vs. being freed) so users know when the wrapped coroutine will actually stop.
- In the Task-bridging examples, you now show both `AsCoroutineInstruction` and `StartTaskAsCoroutine`/`ToCoroutineEnumerator`; it may help readers if you briefly spell out when to prefer each pattern (e.g., embedding inside an existing coroutine vs. starting a top-level coroutine).Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
- 添加了 AsCoroutineInstruction()、ToCoroutineEnumerator() 和 StartTaskAsCoroutine() 的使用场景说明 - 优化了等待异步方法的文档描述,明确不同 API 的适用场景 - 详细说明了 CancelWith 方法的节点有效性判断机制和停止条件 - 补充了 Godot 协程中 Task 转协程的具体使用建议
Summary by Sourcery
更新和扩展协程系统在核心库和 Godot 集成部分的文档,使其与重构后的基于
IEnumerator/IYieldInstruction的调度器以及新的扩展 API 保持一致。文档内容:
CoroutineScheduler/CoroutineHandle/IYieldInstruction模型、分组等待指令,以及与任务(tasks)、事件(events)、命令(commands)、查询(queries)和Mediator的集成。CancelWith进行的节点生命周期绑定、常用等待指令以及基于IContextAware的命令/查询辅助工具。Original summary in English
Summary by Sourcery
Update and expand coroutine system documentation for both core and Godot integrations, aligning it with the refactored IEnumerator/IYieldInstruction-based scheduler and new extension APIs.
Documentation: