面经-unity多线程原理相关

本文最后更新于:2024年12月27日 下午

Unity协程(Coroutine)原理详解

1. 基本原理

协程本质上是一个迭代器(Iterator),它利用了C#的迭代器模式和yield关键字。它不是真正的多线程,而是一种特殊的程序执行方式。

工作流程:

1.暂停和恢复

  • 当遇到yield语句时,协程会保存当前的执行状态

  • 将控制权返回给Unity的主循环

  • 在适当的时机恢复执行

2.状态机

  • 编译器会将协程方法转换为一个状态机

  • 每个yield语句对应一个状态

  • 恢复执行时从上次的状态继续

2. 执行时机

Unity的执行循环:

1
2
3
4
5
6
Update循环:
1. Input Events
2. Update()
3. yield return null 的协程
4. Late Update()
5. 渲染

不同yield指令的执行时机:

  • yield return null - 下一帧Update之前

  • yield return new WaitForFixedUpdate() - 下一次物理更新时

  • yield return new WaitForEndOfFrame() - 当前帧渲染完成后

  • yield return new WaitForSeconds() - 基于Time.time计时

3. 内部实现机制

状态机转换

当你写一个协程时:

1
2
3
4
5
6
IEnumerator MyCoroutine() 
{
Debug.Log("Start");
yield return new WaitForSeconds(1f);
Debug.Log("End");
}

编译器会这样转换成类似的状态机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private sealed class <MyCoroutine>d__1 : IEnumerator 
{
private int state;
private object current;

public <MyCoroutine>d__1(int state)
{
this.state = state;
}

public bool MoveNext()
{
switch (state)
{
case 0:
Debug.Log("Start");
current = new WaitForSeconds(1f);
state = 1;
return true;
case 1:
Debug.Log("End");
state = 2;
return false;
default:
return false;
}
}

public object Current
{
get { return current; }
}
}

4. 内存管理

协程的内存分配:

1.状态机对象

  • 每次启动协程时创建

  • 包含局部变量和执行状态

2.YieldInstruction对象

  • 每个yield return语句可能创建新对象

  • 可以通过缓存减少分配

生命周期:

1.创建:StartCoroutine调用时

2.运行:Unity主循环中调度

3.销毁:

  • 协程完成时

  • 手动停止时

  • MonoBehaviour禁用时

5. 调度机制

Unity的协程调度器:

1.维护一个活动协程列表

2.每帧检查需要执行的协程

3.根据YieldInstruction类型决定执行时机

4.调用MoveNext()推进协程状态

优先级:

1.Update前的协程

2.Update

3.Update后的协程

4.LateUpdate

5.帧结束时的协程

6. 限制和注意事项

技术限制:

1.单线程执行

  • 所有协程在主线程运行

  • 不能进行真正的并行处理

2.状态保存

  • 只能保存基本的执行状态

  • 不保存完整的调用栈

3.异常处理

  • try-catch块跨越yield语句时可能失效

  • 需要特殊的错误处理机制

性能考虑:

1.内存开销

  • 每个活动协程占用内存

  • yield指令可能产生垃圾回收

2.CPU开销

  • 协程调度有额外开销

  • 过多活动协程会影响性能

7. 最佳实践

使用场景:

1.适合用协程的情况:

  • 需要随时间推移的操作

  • 等待特定条件

  • 分帧执行大量工作

2.不适合用协程的情况:

  • CPU密集型计算

  • 需要真正并行的操作

  • 关键性能代码

性能优化:

1.重用YieldInstruction对象

2.适当分批处理

3.及时清理不需要的协程

4.避免过多嵌套

理解协程的这些原理,可以帮助我们更好地使用它,避免常见陷阱,并在适当的场景选择它作为解决方案。


面经-unity多线程原理相关
https://rorschachandbat.github.io/找工作/面经-unity多线程原理相关/
作者
R
发布于
2024年12月25日
许可协议