feat(coroutine): 添加协程分组管理和优先级支持#82
Conversation
- 实现协程分组功能,支持批量暂停、恢复和终止协程 - 添加协程优先级系统,支持从最低到最高的5个优先级级别 - 引入协程统计功能,跟踪启动、完成、失败数量及执行时间 - 添加FakeTimeSource用于协程测试的时间控制 - 实现按优先级排序的协程执行机制 - 添加协程执行时间戳记录功能 - 实现完整的协程统计报告生成功能
|
|
Overall Grade Focus Area: Complexity |
Security Reliability Complexity Hygiene |
Code Review Summary
| Analyzer | Status | Updated (UTC) | Details |
|---|---|---|---|
| C# | Mar 6, 2026 5:07a.m. | Review ↗ | |
| Secrets | Mar 6, 2026 5:07a.m. | Review ↗ |
Reviewer's Guide为调度器新增协程分组和优先级支持,以及可选启用的统计子系统和配套的测试/工具(包括一个伪时间源)。 带统计信息的优先级协程运行与完成时序图sequenceDiagram
actor Client
participant Scheduler as CoroutineScheduler
participant TimeSource as ITimeSource
participant Slot as CoroutineSlot
participant Meta as CoroutineMetadata
participant Stats as CoroutineStatistics
Client->>Scheduler: Run(coroutine, tag, priority, group)
alt coroutine is null
Scheduler-->>Client: return default CoroutineHandle
else coroutine valid
Scheduler->>Scheduler: allocate handle and slotIndex
Scheduler->>Slot: create CoroutineSlot
Scheduler->>Meta: create CoroutineMetadata
Scheduler->>Scheduler: AddTag(tag, handle)
Scheduler->>Scheduler: AddGroup(group, handle)
Scheduler->>Stats: RecordStart(priority, tag)
Scheduler->>Scheduler: Prewarm(slotIndex)
Scheduler->>Scheduler: ActiveCoroutineCount++
Scheduler-->>Client: return handle
end
loop each frame
Client->>Scheduler: Update()
Scheduler->>TimeSource: Update()
TimeSource-->>Scheduler: DeltaTime, CurrentTime
Scheduler->>Scheduler: update Stats.ActiveCount, Stats.PausedCount
Scheduler->>Scheduler: collect running slot indices
Scheduler->>Scheduler: sort indices by Slot.Priority (desc)
loop for each sorted slotIndex
Scheduler->>Slot: advance enumerator
alt coroutine finished
Scheduler->>Scheduler: Complete(slotIndex)
Scheduler->>Meta: read Priority, Tag, StartTime
Scheduler->>TimeSource: CurrentTime
Scheduler->>Stats: RecordComplete(executionTimeMs, Priority, Tag)
Scheduler->>Scheduler: RemoveTag(handle)
Scheduler->>Scheduler: RemoveGroup(handle)
Scheduler->>Scheduler: wake waiting coroutines
Scheduler->>Scheduler: ActiveCoroutineCount--
else coroutine yields
Scheduler->>Slot: update Waiting/State
end
end
end
协程分组暂停、恢复和终止操作时序图sequenceDiagram
actor Client
participant Scheduler as CoroutineScheduler
Client->>Scheduler: PauseGroup(groupName)
alt group exists
Scheduler->>Scheduler: lookup _grouped[groupName]
Scheduler->>Scheduler: for each handle in set
loop handles
Scheduler->>Scheduler: Pause(handle)
end
Scheduler-->>Client: return pausedCount
else group missing
Scheduler-->>Client: return 0
end
Client->>Scheduler: ResumeGroup(groupName)
alt group exists
Scheduler->>Scheduler: for each handle in set
loop handles
Scheduler->>Scheduler: Resume(handle)
end
Scheduler-->>Client: return resumedCount
else group missing
Scheduler-->>Client: return 0
end
Client->>Scheduler: KillGroup(groupName)
alt group exists
Scheduler->>Scheduler: for each handle in set
loop handles
Scheduler->>Scheduler: Kill(handle)
end
Scheduler-->>Client: return killedCount
else group missing
Scheduler-->>Client: return 0
end
带分组、优先级和统计的协程调度器类图classDiagram
direction LR
class CoroutineScheduler {
<<sealed>>
+CoroutineScheduler(ITimeSource timeSource, byte instanceId, int initialCapacity, bool enableStatistics)
+int ActiveCoroutineCount
+ICoroutineStatistics Statistics
+event OnCoroutineException
+CoroutineHandle Run(IEnumerator~IYieldInstruction~ coroutine, string tag, CoroutinePriority priority, string group)
+void Update()
+bool Pause(CoroutineHandle handle)
+bool Resume(CoroutineHandle handle)
+bool Kill(CoroutineHandle handle)
+int PauseGroup(string group)
+int ResumeGroup(string group)
+int KillGroup(string group)
+int GetGroupCount(string group)
+int KillByTag(string tag)
+int Clear()
-Dictionary~CoroutineHandle,CoroutineMetadata~ _metadata
-Dictionary~string,HashSet~CoroutineHandle~~ _tagged
-Dictionary~string,HashSet~CoroutineHandle~~ _grouped
-Dictionary~CoroutineHandle,HashSet~CoroutineHandle~~ _waiting
-CoroutineSlot[] _slots
-CoroutineStatistics _statistics
-ITimeSource _timeSource
-ILogger _logger
-int _nextSlot
-void Complete(int slotIndex)
-void OnError(int slotIndex, Exception ex)
-void AddTag(string tag, CoroutineHandle handle)
-void RemoveTag(CoroutineHandle handle)
-void AddGroup(string group, CoroutineHandle handle)
-void RemoveGroup(CoroutineHandle handle)
}
class CoroutineMetadata {
string Group
CoroutinePriority Priority
int SlotIndex
double StartTime
CoroutineState State
string Tag
}
class CoroutineSlot {
IEnumerator~IYieldInstruction~ Enumerator
CoroutineHandle Handle
bool HasStarted
CoroutinePriority Priority
CoroutineState State
IYieldInstruction Waiting
}
class CoroutineStatistics {
<<sealed>>
+long TotalStarted
+long TotalCompleted
+long TotalFailed
+int ActiveCount
+int PausedCount
+double AverageExecutionTimeMs
+double MaxExecutionTimeMs
+int GetCountByPriority(CoroutinePriority priority)
+int GetCountByTag(string tag)
+void Reset()
+string GenerateReport()
+void RecordStart(CoroutinePriority priority, string tag)
+void RecordComplete(double executionTimeMs, CoroutinePriority priority, string tag)
+void RecordFailure(CoroutinePriority priority, string tag)
-Dictionary~CoroutinePriority,int~ _countByPriority
-Dictionary~string,int~ _countByTag
-object _lock
-double _maxExecutionTimeMs
-long _totalStarted
-long _totalCompleted
-long _totalFailed
-long _totalExecutionTimeMs
}
class ICoroutineStatistics {
<<interface>>
+long TotalStarted
+long TotalCompleted
+long TotalFailed
+int ActiveCount
+int PausedCount
+double AverageExecutionTimeMs
+double MaxExecutionTimeMs
+int GetCountByPriority(CoroutinePriority priority)
+int GetCountByTag(string tag)
+void Reset()
+string GenerateReport()
}
class ITimeSource {
<<interface>>
+double CurrentTime
+double DeltaTime
+void Update()
}
class FakeTimeSource {
<<sealed>>
+double CurrentTime
+double DeltaTime
+void Update()
+void Advance(double deltaTime)
+void Reset()
}
class CoroutinePriority {
<<enum>>
Lowest
Low
Normal
High
Highest
}
class CoroutineHandle
class CoroutineState
class IYieldInstruction
class ILogger
CoroutineScheduler --> ITimeSource : uses
CoroutineScheduler --> ICoroutineStatistics : exposes
CoroutineScheduler --> CoroutineStatistics : owns (optional)
CoroutineScheduler --> CoroutineMetadata : metadata
CoroutineScheduler --> CoroutineSlot : slots
CoroutineScheduler --> CoroutinePriority : scheduling
CoroutineMetadata --> CoroutinePriority : priority
CoroutineMetadata --> CoroutineState : state
CoroutineSlot --> CoroutineHandle : handle
CoroutineSlot --> CoroutinePriority : priority
CoroutineSlot --> CoroutineState : state
CoroutineSlot --> IYieldInstruction : enumerator yields
CoroutineStatistics ..|> ICoroutineStatistics
FakeTimeSource ..|> ITimeSource
ICoroutineStatistics --> CoroutinePriority : in methods
ICoroutineStatistics --> string : tag
CoroutineScheduler --> CoroutineHandle : returns
CoroutineScheduler --> IYieldInstruction : runs
CoroutineScheduler --> ILogger : logging
CoroutineStatistics --> string : report
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
Original review guide in EnglishReviewer's GuideAdds coroutine grouping and priority support to the scheduler, along with an opt-in statistics subsystem and supporting tests/utilities (including a fake time source). Sequence diagram for running and completing a prioritized coroutine with statisticssequenceDiagram
actor Client
participant Scheduler as CoroutineScheduler
participant TimeSource as ITimeSource
participant Slot as CoroutineSlot
participant Meta as CoroutineMetadata
participant Stats as CoroutineStatistics
Client->>Scheduler: Run(coroutine, tag, priority, group)
alt coroutine is null
Scheduler-->>Client: return default CoroutineHandle
else coroutine valid
Scheduler->>Scheduler: allocate handle and slotIndex
Scheduler->>Slot: create CoroutineSlot
Scheduler->>Meta: create CoroutineMetadata
Scheduler->>Scheduler: AddTag(tag, handle)
Scheduler->>Scheduler: AddGroup(group, handle)
Scheduler->>Stats: RecordStart(priority, tag)
Scheduler->>Scheduler: Prewarm(slotIndex)
Scheduler->>Scheduler: ActiveCoroutineCount++
Scheduler-->>Client: return handle
end
loop each frame
Client->>Scheduler: Update()
Scheduler->>TimeSource: Update()
TimeSource-->>Scheduler: DeltaTime, CurrentTime
Scheduler->>Scheduler: update Stats.ActiveCount, Stats.PausedCount
Scheduler->>Scheduler: collect running slot indices
Scheduler->>Scheduler: sort indices by Slot.Priority (desc)
loop for each sorted slotIndex
Scheduler->>Slot: advance enumerator
alt coroutine finished
Scheduler->>Scheduler: Complete(slotIndex)
Scheduler->>Meta: read Priority, Tag, StartTime
Scheduler->>TimeSource: CurrentTime
Scheduler->>Stats: RecordComplete(executionTimeMs, Priority, Tag)
Scheduler->>Scheduler: RemoveTag(handle)
Scheduler->>Scheduler: RemoveGroup(handle)
Scheduler->>Scheduler: wake waiting coroutines
Scheduler->>Scheduler: ActiveCoroutineCount--
else coroutine yields
Scheduler->>Slot: update Waiting/State
end
end
end
Sequence diagram for coroutine group pause, resume, and kill operationssequenceDiagram
actor Client
participant Scheduler as CoroutineScheduler
Client->>Scheduler: PauseGroup(groupName)
alt group exists
Scheduler->>Scheduler: lookup _grouped[groupName]
Scheduler->>Scheduler: for each handle in set
loop handles
Scheduler->>Scheduler: Pause(handle)
end
Scheduler-->>Client: return pausedCount
else group missing
Scheduler-->>Client: return 0
end
Client->>Scheduler: ResumeGroup(groupName)
alt group exists
Scheduler->>Scheduler: for each handle in set
loop handles
Scheduler->>Scheduler: Resume(handle)
end
Scheduler-->>Client: return resumedCount
else group missing
Scheduler-->>Client: return 0
end
Client->>Scheduler: KillGroup(groupName)
alt group exists
Scheduler->>Scheduler: for each handle in set
loop handles
Scheduler->>Scheduler: Kill(handle)
end
Scheduler-->>Client: return killedCount
else group missing
Scheduler-->>Client: return 0
end
Class diagram for coroutine scheduler with grouping, priority, and statisticsclassDiagram
direction LR
class CoroutineScheduler {
<<sealed>>
+CoroutineScheduler(ITimeSource timeSource, byte instanceId, int initialCapacity, bool enableStatistics)
+int ActiveCoroutineCount
+ICoroutineStatistics Statistics
+event OnCoroutineException
+CoroutineHandle Run(IEnumerator~IYieldInstruction~ coroutine, string tag, CoroutinePriority priority, string group)
+void Update()
+bool Pause(CoroutineHandle handle)
+bool Resume(CoroutineHandle handle)
+bool Kill(CoroutineHandle handle)
+int PauseGroup(string group)
+int ResumeGroup(string group)
+int KillGroup(string group)
+int GetGroupCount(string group)
+int KillByTag(string tag)
+int Clear()
-Dictionary~CoroutineHandle,CoroutineMetadata~ _metadata
-Dictionary~string,HashSet~CoroutineHandle~~ _tagged
-Dictionary~string,HashSet~CoroutineHandle~~ _grouped
-Dictionary~CoroutineHandle,HashSet~CoroutineHandle~~ _waiting
-CoroutineSlot[] _slots
-CoroutineStatistics _statistics
-ITimeSource _timeSource
-ILogger _logger
-int _nextSlot
-void Complete(int slotIndex)
-void OnError(int slotIndex, Exception ex)
-void AddTag(string tag, CoroutineHandle handle)
-void RemoveTag(CoroutineHandle handle)
-void AddGroup(string group, CoroutineHandle handle)
-void RemoveGroup(CoroutineHandle handle)
}
class CoroutineMetadata {
string Group
CoroutinePriority Priority
int SlotIndex
double StartTime
CoroutineState State
string Tag
}
class CoroutineSlot {
IEnumerator~IYieldInstruction~ Enumerator
CoroutineHandle Handle
bool HasStarted
CoroutinePriority Priority
CoroutineState State
IYieldInstruction Waiting
}
class CoroutineStatistics {
<<sealed>>
+long TotalStarted
+long TotalCompleted
+long TotalFailed
+int ActiveCount
+int PausedCount
+double AverageExecutionTimeMs
+double MaxExecutionTimeMs
+int GetCountByPriority(CoroutinePriority priority)
+int GetCountByTag(string tag)
+void Reset()
+string GenerateReport()
+void RecordStart(CoroutinePriority priority, string tag)
+void RecordComplete(double executionTimeMs, CoroutinePriority priority, string tag)
+void RecordFailure(CoroutinePriority priority, string tag)
-Dictionary~CoroutinePriority,int~ _countByPriority
-Dictionary~string,int~ _countByTag
-object _lock
-double _maxExecutionTimeMs
-long _totalStarted
-long _totalCompleted
-long _totalFailed
-long _totalExecutionTimeMs
}
class ICoroutineStatistics {
<<interface>>
+long TotalStarted
+long TotalCompleted
+long TotalFailed
+int ActiveCount
+int PausedCount
+double AverageExecutionTimeMs
+double MaxExecutionTimeMs
+int GetCountByPriority(CoroutinePriority priority)
+int GetCountByTag(string tag)
+void Reset()
+string GenerateReport()
}
class ITimeSource {
<<interface>>
+double CurrentTime
+double DeltaTime
+void Update()
}
class FakeTimeSource {
<<sealed>>
+double CurrentTime
+double DeltaTime
+void Update()
+void Advance(double deltaTime)
+void Reset()
}
class CoroutinePriority {
<<enum>>
Lowest
Low
Normal
High
Highest
}
class CoroutineHandle
class CoroutineState
class IYieldInstruction
class ILogger
CoroutineScheduler --> ITimeSource : uses
CoroutineScheduler --> ICoroutineStatistics : exposes
CoroutineScheduler --> CoroutineStatistics : owns (optional)
CoroutineScheduler --> CoroutineMetadata : metadata
CoroutineScheduler --> CoroutineSlot : slots
CoroutineScheduler --> CoroutinePriority : scheduling
CoroutineMetadata --> CoroutinePriority : priority
CoroutineMetadata --> CoroutineState : state
CoroutineSlot --> CoroutineHandle : handle
CoroutineSlot --> CoroutinePriority : priority
CoroutineSlot --> CoroutineState : state
CoroutineSlot --> IYieldInstruction : enumerator yields
CoroutineStatistics ..|> ICoroutineStatistics
FakeTimeSource ..|> ITimeSource
ICoroutineStatistics --> CoroutinePriority : in methods
ICoroutineStatistics --> string : tag
CoroutineScheduler --> CoroutineHandle : returns
CoroutineScheduler --> IYieldInstruction : runs
CoroutineScheduler --> ILogger : logging
CoroutineStatistics --> string : report
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - 我发现了两个问题,并提供了一些高层次反馈:
- 在
KillGroup和KillByTag中,你在遍历HashSet<CoroutineHandle>时调用Kill,而Kill可能会通过RemoveGroup/RemoveTag间接从同一个集合中移除句柄,这有触发InvalidOperationException的风险;建议在遍历前先复制到数组(就像之前的KillByTag所做的那样)。 CoroutineStatistics类的文档说明它是线程安全的,但ActiveCount和PausedCount是普通的自动属性,由调度器直接更新,未使用 Interlocked 或锁,因此并发读取可能会看到撕裂或过期的值;要么让这些更新变成原子/线程安全的,要么在文档中澄清该接口只是部分线程安全。- 在
CoroutineScheduler.Update中,每帧通过_metadata.Count(m => m.Value.State == CoroutineState.Paused)计算PausedCount会带来每帧分配和 O(n) 的开销;你可以通过维护一个在Pause/Resume中更新的暂停计数器来避免这部分开销。
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `KillGroup` and `KillByTag`, you iterate the `HashSet<CoroutineHandle>` while `Kill` can indirectly remove handles from the same set via `RemoveGroup/RemoveTag`, which risks `InvalidOperationException`; consider copying to an array (as the previous `KillByTag` did) before iterating.
- The `CoroutineStatistics` class is documented as thread‑safe, but `ActiveCount` and `PausedCount` are plain auto‑properties updated from the scheduler without interlocked or locking, so any concurrent readers may see torn or out‑of‑date values; either make these updates atomic/thread‑safe or clarify that the interface is only partially thread‑safe.
- In `CoroutineScheduler.Update`, computing `PausedCount` each frame with `_metadata.Count(m => m.Value.State == CoroutineState.Paused)` adds per‑frame allocations and O(n) cost; you can avoid this overhead by maintaining a paused counter updated in `Pause`/`Resume` instead.
## Individual Comments
### Comment 1
<location path="GFramework.Core/coroutine/CoroutineScheduler.cs" line_range="343" />
<code_context>
+ if (!_grouped.TryGetValue(group, out var handles))
+ return 0;
+
+ return handles.Count(Kill);
+ }
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Using `handles.Count(Kill)` risks modifying the collection while iterating it.
In `KillByTag`, `handles` is the same `HashSet<CoroutineHandle>` stored in `_tagged`, and `Kill` is likely to call `RemoveTag`, mutating this set while `Count` is enumerating it. This can cause an `InvalidOperationException` ("Collection was modified"). Please keep the previous defensive pattern (e.g. copy to an array first) or otherwise ensure enumeration happens on a stable collection.
</issue_to_address>
### Comment 2
<location path="GFramework.Core/coroutine/CoroutineStatistics.cs" line_range="31-34" />
<code_context>
+ public long TotalFailed => Interlocked.Read(ref _totalFailed);
+
+ /// <inheritdoc />
+ public int ActiveCount { get; set; }
+
+ /// <inheritdoc />
+ public int PausedCount { get; set; }
+
+ /// <inheritdoc />
</code_context>
<issue_to_address>
**issue (bug_risk):** ActiveCount/PausedCount are not updated in a thread-safe way despite the class being documented as thread-safe.
These properties are written from `CoroutineScheduler.Update` with no synchronization and also modified in `Reset` under a lock, so they are neither consistently atomic nor consistently protected. That violates the documented thread-safety of `CoroutineStatistics` and can cause visibility races. Please either:
- back them with `Interlocked` fields (like the other counters), or
- make them part of the locked state, only accessing the backing fields under the existing `_lock`.
This ensures concurrent reads don’t race with writes.
</issue_to_address>帮我变得更有用!请对每条评论点击 👍 或 👎,我会根据这些反馈改进后续的评审。
Original comment in English
Hey - I've found 2 issues, and left some high level feedback:
- In
KillGroupandKillByTag, you iterate theHashSet<CoroutineHandle>whileKillcan indirectly remove handles from the same set viaRemoveGroup/RemoveTag, which risksInvalidOperationException; consider copying to an array (as the previousKillByTagdid) before iterating. - The
CoroutineStatisticsclass is documented as thread‑safe, butActiveCountandPausedCountare plain auto‑properties updated from the scheduler without interlocked or locking, so any concurrent readers may see torn or out‑of‑date values; either make these updates atomic/thread‑safe or clarify that the interface is only partially thread‑safe. - In
CoroutineScheduler.Update, computingPausedCounteach frame with_metadata.Count(m => m.Value.State == CoroutineState.Paused)adds per‑frame allocations and O(n) cost; you can avoid this overhead by maintaining a paused counter updated inPause/Resumeinstead.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `KillGroup` and `KillByTag`, you iterate the `HashSet<CoroutineHandle>` while `Kill` can indirectly remove handles from the same set via `RemoveGroup/RemoveTag`, which risks `InvalidOperationException`; consider copying to an array (as the previous `KillByTag` did) before iterating.
- The `CoroutineStatistics` class is documented as thread‑safe, but `ActiveCount` and `PausedCount` are plain auto‑properties updated from the scheduler without interlocked or locking, so any concurrent readers may see torn or out‑of‑date values; either make these updates atomic/thread‑safe or clarify that the interface is only partially thread‑safe.
- In `CoroutineScheduler.Update`, computing `PausedCount` each frame with `_metadata.Count(m => m.Value.State == CoroutineState.Paused)` adds per‑frame allocations and O(n) cost; you can avoid this overhead by maintaining a paused counter updated in `Pause`/`Resume` instead.
## Individual Comments
### Comment 1
<location path="GFramework.Core/coroutine/CoroutineScheduler.cs" line_range="343" />
<code_context>
+ if (!_grouped.TryGetValue(group, out var handles))
+ return 0;
+
+ return handles.Count(Kill);
+ }
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Using `handles.Count(Kill)` risks modifying the collection while iterating it.
In `KillByTag`, `handles` is the same `HashSet<CoroutineHandle>` stored in `_tagged`, and `Kill` is likely to call `RemoveTag`, mutating this set while `Count` is enumerating it. This can cause an `InvalidOperationException` ("Collection was modified"). Please keep the previous defensive pattern (e.g. copy to an array first) or otherwise ensure enumeration happens on a stable collection.
</issue_to_address>
### Comment 2
<location path="GFramework.Core/coroutine/CoroutineStatistics.cs" line_range="31-34" />
<code_context>
+ public long TotalFailed => Interlocked.Read(ref _totalFailed);
+
+ /// <inheritdoc />
+ public int ActiveCount { get; set; }
+
+ /// <inheritdoc />
+ public int PausedCount { get; set; }
+
+ /// <inheritdoc />
</code_context>
<issue_to_address>
**issue (bug_risk):** ActiveCount/PausedCount are not updated in a thread-safe way despite the class being documented as thread-safe.
These properties are written from `CoroutineScheduler.Update` with no synchronization and also modified in `Reset` under a lock, so they are neither consistently atomic nor consistently protected. That violates the documented thread-safety of `CoroutineStatistics` and can cause visibility races. Please either:
- back them with `Interlocked` fields (like the other counters), or
- make them part of the locked state, only accessing the backing fields under the existing `_lock`.
This ensures concurrent reads don’t race with writes.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
- 将 CoroutinePriority 枚举的基础类型从 byte 移除 - 优化枚举类型的定义方式
- 引入 _pausedCount 字段直接跟踪暂停协程数量 - 将统计信息中的 ActiveCount 和 PausedCount 改为线程安全的原子操作 - 在暂停和恢复协程时直接更新 _pausedCount 计数 - 修复 KillGroup 方法中的并发修改异常问题 - 重置统计信息时使用原子操作清零计数字段
- 在KillByTag方法中创建句柄数组副本以避免集合被修改的异常 - 修复Complete方法的缩进格式问题 - 为WaitCommandSwitch添加默认分支以处理未知类型的等待指令
Summary by Sourcery
为调度器新增协程分组、优先级与统计能力,以及相应的抽象与测试。
New Features:
FakeTimeSource实现,用于驱动可确定性的协程测试。Enhancements:
Tests:
Original summary in English
Summary by Sourcery
Add coroutine grouping, prioritization, and statistics capabilities to the scheduler, along with supporting abstractions and tests.
New Features:
Enhancements:
Tests: