从C#迭代器到Unity协程:揭秘yield return背后的状态机魔法
1. 协程的本质:单线程下的异步魔法
在游戏开发中,我们常常需要处理各种异步操作,比如资源加载、动画播放、延时触发等。传统多线程方案在Unity中往往行不通,因为Unity的API大多只能在主线程调用。这就是协程(Coroutine)大显身手的地方。
协程本质上是一种基于迭代器的异步编程模式,它允许我们将一个长任务分割成多个小步骤,在多个帧中逐步执行。与线程不同,协程始终运行在主线程上,通过yield指令实现执行权的让渡和恢复。
IEnumerator SimpleCoroutine()
{
Debug.Log("第一步");
yield return null; // 暂停一帧
Debug.Log("第二步");
yield return new WaitForSeconds(1f); // 暂停1秒
Debug.Log("第三步");
}
这段代码展示了协程的三个关键特性:
- 使用IEnumerator作为返回类型
- 通过yield return控制执行流程
- 可以暂停任意时长后继续执行
2. 编译器魔法:yield如何变身状态机
当我们在C#中使用yield关键字时,编译器会进行一系列神奇的转换。让我们通过反编译看看背后的实现机制。
原始代码:
IEnumerator CountToTen()
{
for(int i=1; i<=10; i++)
{
yield return i;
}
}
编译器生成的等价代码:
private sealed class <CountToTen>d__0 : IEnumerator<object>, IEnumerator
{
private int <>1__state;
private object <>2__current;
private int <>l__initialThreadId;
private int <i>5__1;
public <CountToTen>d__0(int <>1__state)
{
this.<>1__state = <>1__state;
this.<>l__initialThreadId = Environment.CurrentManagedThreadId;
}
private bool MoveNext()
{
switch(this.<>1__state)
{
case 0:
this.<>1__state = -1;
this.<i>5__1 = 1;
while(this.<i>5__1 <= 10)
{
this.<>2__current = this.<i>5__1;
this.<>1__state = 1;
return true;
Label_004B:
this.<>1__state = -1;
this.<i>5__1++;
}
break;
case 1:
goto Label_004B;
}
return false;
}
}
关键点解析:
- 编译器生成一个嵌套类实现状态机
- state字段记录当前执行位置
- MoveNext方法包含所有执行逻辑
- 每次yield return对应一个case分支
- 局部变量被提升为类的字段
3. Unity的协程调度机制
Unity引擎内部维护着一个协程调度系统,其核心工作原理如下:
-
启动阶段:
- StartCoroutine将IEnumerator包装成Coroutine对象
- 该对象被添加到当前MonoBehaviour的活跃协程列表
- 立即执行第一次MoveNext()
-
更新阶段:
- 每帧结束后,Unity检查所有活跃协程
- 根据yield return的指令类型决定何时恢复执行:
Yield指令类型 恢复条件 null 下一帧立即恢复 WaitForSeconds 指定时间后恢复 WaitForEndOfFrame 渲染结束后恢复 WWW/AsyncOperation 异步操作完成后恢复 其他Coroutine 等待嵌套协程完成 -
停止条件:
- MoveNext()返回false
- 调用StopCoroutine
- GameObject被销毁或禁用
// Unity内部伪代码
void UpdateCoroutin


5020

被折叠的 条评论
为什么被折叠?



