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 riderModule.iml
/_ReSharper.Caches/ /_ReSharper.Caches/
GFramework.sln.DotSettings.user GFramework.sln.DotSettings.user
.idea/ .idea/
opencode.json

View File

@ -111,33 +111,85 @@ public class CoroutineHandle : IYieldInstruction, ICoroutineHandle
{ {
if (IsDone) return false; if (IsDone) return false;
// 检查并更新当前等待的指令 // 如果有等待指令,先更新等待指令
if (_waitingInstruction != null) if (_waitingInstruction != null)
{ {
_waitingInstruction.Update(deltaTime); _waitingInstruction.Update(deltaTime);
if (!_waitingInstruction.IsDone) return true;
// 如果等待指令还未完成,继续等待
if (!_waitingInstruction.IsDone)
{
return true;
}
// 等待指令已完成,清除它
_waitingInstruction = null; _waitingInstruction = null;
} }
// 循环执行直到需要等待或协程完成 // 每帧只推进一步(执行一个 MoveNext
while (_stack.Count > 0 && !IsDone) if (_stack.Count > 0)
{ {
try try
{ {
var current = _stack.Peek(); 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; if (needsWait)
{
// 否则继续执行下一个步骤 return true;
continue;
} }
_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) catch (Exception ex)
{ {
@ -146,13 +198,9 @@ public class CoroutineHandle : IYieldInstruction, ICoroutineHandle
} }
} }
if (_stack.Count == 0) // 栈为空,协程完成
{ Complete();
Complete(); return false;
return false;
}
return true;
} }
/// <summary> /// <summary>
@ -165,25 +213,31 @@ public class CoroutineHandle : IYieldInstruction, ICoroutineHandle
switch (yielded) switch (yielded)
{ {
case CoroutineHandle otherHandle: case CoroutineHandle otherHandle:
// 标记子协程由父协程管理 // 处理 yield return CoroutineHandle
if (!otherHandle.IsDone) if (otherHandle.IsDone)
{ {
otherHandle.MarkAsManagedByParent(); // 子协程已完成,不需要等待
_waitingInstruction = otherHandle; return false;
return true; // 需要等待子协程完成
} }
return false; // 子协程已完成,不需要等待
// 标记子协程由父协程管理
otherHandle.MarkAsManagedByParent();
_waitingInstruction = otherHandle;
return true; // 需要等待子协程完成
case IEnumerator nested: case IEnumerator nested:
// 处理 yield return IEnumerator嵌套协程
_stack.Push(nested); _stack.Push(nested);
return false; // 压入嵌套协程,立即执行 return false; // 压入嵌套协程,在下一帧继续执行
case IYieldInstruction instruction: case IYieldInstruction instruction:
// 处理 yield return IYieldInstruction
_waitingInstruction = instruction; _waitingInstruction = instruction;
return true; // 需要等待指令完成 return true; // 需要等待指令完成
case null: case null:
return false; // null立即继续 // 处理 yield return null等待一帧
return false; // null 立即继续,但会返回 true 让协程继续执行
default: default:
throw new InvalidOperationException($"Unsupported yield type: {yielded.GetType()}"); throw new InvalidOperationException($"Unsupported yield type: {yielded.GetType()}");

View File

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

View File

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

View File

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