mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
feat(coroutine): 优化协程执行机制,修复嵌套协程和yield null处理问题
- 修改CoroutineHandle中的协程执行逻辑,实现每帧只推进一步的机制, 避免单帧内过度执行导致的性能问题 - 修复yield null处理逻辑,正确处理等待一帧后继续执行的情况 - 改进嵌套协程处理,确保子协程完成后正确返回到父协程 - 优化WaitUntil条件检查逻辑,确保状态转换正确性 - 在定时任务扩展中添加yield break确保协程正确结束 - 调整协程调度器的更新顺序,先添加新协程再执行更新
This commit is contained in:
parent
f24ec656e6
commit
3e26c84bb1
3
.gitignore
vendored
3
.gitignore
vendored
@ -4,4 +4,5 @@ obj/
|
||||
riderModule.iml
|
||||
/_ReSharper.Caches/
|
||||
GFramework.sln.DotSettings.user
|
||||
.idea/
|
||||
.idea/
|
||||
opencode.json
|
||||
@ -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()}");
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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(); // 先等待,再执行
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -19,7 +19,7 @@ public class WaitUntil(Func<bool> predicate) : IYieldInstruction
|
||||
/// <param name="deltaTime">时间增量</param>
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
// 只有在未完成状态下才检查条件
|
||||
// 每次更新都重新评估条件,但一旦完成就保持完成状态
|
||||
if (!IsDone) IsDone = predicate();
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user