feat(coroutine): 优化协程执行机制,修复嵌套协程和yield null处理问题

- 修改CoroutineHandle中的协程执行逻辑,实现每帧只推进一步的机制,
避免单帧内过度执行导致的性能问题
- 修复yield null处理逻辑,正确处理等待一帧后继续执行的情况
- 改进嵌套协程处理,确保子协程完成后正确返回到父协程
- 优化WaitUntil条件检查逻辑,确保状态转换正确性
- 在定时任务扩展中添加yield break确保协程正确结束
- 调整协程调度器的更新顺序,先添加新协程再执行更新
This commit is contained in:
GeWuYou 2026-01-21 14:19:15 +08:00
parent f24ec656e6
commit 3e26c84bb1
5 changed files with 103 additions and 36 deletions

3
.gitignore vendored
View File

@ -4,4 +4,5 @@ obj/
riderModule.iml
/_ReSharper.Caches/
GFramework.sln.DotSettings.user
.idea/
.idea/
opencode.json

View File

@ -111,33 +111,85 @@ public class CoroutineHandle : IYieldInstruction, ICoroutineHandle
{
if (IsDone) return false;
// 检查并更新当前等待的指令
// 如果有等待指令,先更新等待指令
if (_waitingInstruction != null)
{
_waitingInstruction.Update(deltaTime);
if (!_waitingInstruction.IsDone) return true;
// 如果等待指令还未完成,继续等待
if (!_waitingInstruction.IsDone)
{
return true;
}
// 等待指令已完成,清除它
_waitingInstruction = null;
}
// 循环执行直到需要等待或协程完成
while (_stack.Count > 0 && !IsDone)
// 每帧只推进一步(执行一个 MoveNext
if (_stack.Count > 0)
{
try
{
var current = _stack.Peek();
if (current.MoveNext())
bool hasNext = current.MoveNext();
if (!hasNext)
{
var yielded = current.Current;
var needsWait = ProcessYieldValue(yielded);
// 当前枚举器已完成,弹出栈
_stack.Pop();
// 如果栈为空,协程完成
if (_stack.Count == 0)
{
Complete();
return false;
}
// 否则继续执行下一个枚举器(在下一帧)
return true;
}
// MoveNext() 返回 true有下一个值
var yielded = current.Current;
var needsWait = ProcessYieldValue(yielded);
// 如果需要等待,则暂停执行
if (needsWait) return true;
// 否则继续执行下一个步骤
continue;
// 如果需要等待,则暂停执行
if (needsWait)
{
return true;
}
_stack.Pop();
// 如果不需要等待yield null 或嵌套协程),继续处理
// 处理 yield null 的情况yield null 后需要再调用一次 MoveNext 才能知道是否完成
if (yielded == null)
{
// yield null 意味着等待一帧,但协程可能还没有完成
// 需要再次检查是否还有更多步骤
bool stillHasNext = current.MoveNext();
if (!stillHasNext)
{
// 协程确实完成了
_stack.Pop();
if (_stack.Count == 0)
{
Complete();
return false;
}
}
else
{
// 还有更多内容yield 出来的值需要处理
yielded = current.Current;
needsWait = ProcessYieldValue(yielded);
if (needsWait)
{
return true;
}
}
}
return true;
}
catch (Exception ex)
{
@ -146,13 +198,9 @@ public class CoroutineHandle : IYieldInstruction, ICoroutineHandle
}
}
if (_stack.Count == 0)
{
Complete();
return false;
}
return true;
// 栈为空,协程完成
Complete();
return false;
}
/// <summary>
@ -165,25 +213,31 @@ public class CoroutineHandle : IYieldInstruction, ICoroutineHandle
switch (yielded)
{
case CoroutineHandle otherHandle:
// 标记子协程由父协程管理
if (!otherHandle.IsDone)
// 处理 yield return CoroutineHandle
if (otherHandle.IsDone)
{
otherHandle.MarkAsManagedByParent();
_waitingInstruction = otherHandle;
return true; // 需要等待子协程完成
// 子协程已完成,不需要等待
return false;
}
return false; // 子协程已完成,不需要等待
// 标记子协程由父协程管理
otherHandle.MarkAsManagedByParent();
_waitingInstruction = otherHandle;
return true; // 需要等待子协程完成
case IEnumerator nested:
// 处理 yield return IEnumerator嵌套协程
_stack.Push(nested);
return false; // 压入嵌套协程,立即执行
return false; // 压入嵌套协程,在下一帧继续执行
case IYieldInstruction instruction:
// 处理 yield return IYieldInstruction
_waitingInstruction = instruction;
return true; // 需要等待指令完成
case null:
return false; // null立即继续
// 处理 yield return null等待一帧
return false; // null 立即继续,但会返回 true 让协程继续执行
default:
throw new InvalidOperationException($"Unsupported yield type: {yielded.GetType()}");

View File

@ -25,16 +25,19 @@ public class CoroutineScheduler : ICoroutineScheduler
$"Owner: {_ownerThreadId}, Current: {Thread.CurrentThread.ManagedThreadId}");
}
// 先将新协程添加到活动列表
if (_toAdd.Count > 0)
{
_active.AddRange(_toAdd);
_toAdd.Clear();
}
// 遍历活动协程,每帧只推进一步
for (var i = _active.Count - 1; i >= 0; i--)
{
var c = _active[i];
// 检查作用域是否仍然活跃
if (!c.Context.Scope.IsActive)
{
c.Cancel();
@ -46,21 +49,29 @@ public class CoroutineScheduler : ICoroutineScheduler
if (c.IsManagedByParent)
continue;
// 更新协程,每帧只推进一步
((IYieldInstruction)c).Update(deltaTime);
// 如果协程完成,标记为待移除
if (c.IsDone)
_toRemove.Add(c);
}
if (_toRemove.Count <= 0) return;
_active.RemoveAll(c => _toRemove.Contains(c));
_toRemove.Clear();
// 移除已完成的协程
if (_toRemove.Count > 0)
{
_active.RemoveAll(c => _toRemove.Contains(c));
_toRemove.Clear();
}
}
internal CoroutineHandle StartCoroutine(IEnumerator routine, CoroutineContext context)
{
var handle = new CoroutineHandle(routine, context, null);
// 添加到调度队列,协程将在下次 Update 时开始执行
_toAdd.Add(handle);
return handle;
}

View File

@ -30,6 +30,7 @@ public static class CoroutineScopeExtensions
{
yield return new WaitForSeconds(delay);
action?.Invoke();
yield break; // 确保协程正确结束
}
/// <summary>
@ -52,11 +53,11 @@ public static class CoroutineScopeExtensions
/// <returns>协程迭代器</returns>
private static IEnumerator RepeatingRoutine(float interval, Action action)
{
// 持续循环执行动作并等待指定间隔
// 持续循环等待指定间隔后执行动作
while (true)
{
action?.Invoke();
yield return new WaitForSeconds(interval);
action?.Invoke(); // 先等待,再执行
}
}
}

View File

@ -19,7 +19,7 @@ public class WaitUntil(Func<bool> predicate) : IYieldInstruction
/// <param name="deltaTime">时间增量</param>
public void Update(float deltaTime)
{
// 只有在未完成状态下才检查条件
// 每次更新都重新评估条件,但一旦完成就保持完成状态
if (!IsDone) IsDone = predicate();
}