Unity中的协程

协程的基础

  1. 协程 是用户态的轻量级线程,线程内部调度的基本单位,拥有自己的寄存器上下文和栈。

  2. 同一时间只能执行一个协程,而其他协程处于休眠状态,适合对任务进行分时处理。对于 Unity 来说协程是单线程的,在主线程中完成。

  3. 切换时先将寄存器上下文和栈保存,等切换回来的时候再进行恢复。也就是说协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态。由于直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快

协程的使用

协程就像一个函数,能够暂停执行并将控制权返还给 Unity,然后在达到条件时被挂起,继续执行后面的部分。

  1. 挂起协程
1
2
public Coroutine StartCoroutine(IEnumerator routine)
public Coroutine StartCoroutine(string methodName, object value = null)
  1. 终止协程
1
2
3
public void StopCoroutine(IEnumerator routine);
public void StopCoroutine(string methodName);
public void StopAllCoroutines();

协程的原理

IEnumerator

协程的本质是一个 IEnumerator (迭代器)。

1
2
3
4
5
6
public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}

Unity 在每帧做的工作就是:调用 IEnumeratorMoveNext() 方法,如果返回 true ,就从当前位置继续往下执行;如果是返回 false,就是结束迭代器。

Yield

yield 的本质是语法糖,通过实现 IEnumerator 来实现迭代功能。

在协程中使用 yield return 会返回一个 IEnumerator 对象。

Unity 每帧调用这个 IEnumerator 对象的 MoveNext() 方法。

当这个 IEnumerator 对象的 MoveNext() 返回 false 时(也就是说这个 IEnumertatorCurrent 已经迭代到最后一个元素),Unity 就会将这个 IEnumeratorCortoutines list 中移除。

移除了这个 IEnumerator 对象,也就可以接着执行后面的语句了(也就是 yield return 后面的语句)。

Unity 中实现了一个 YieldInstruction,是所有 yield 指令的基类。本质上也可以看作一个函数体,每帧 MoveNext() 检查是否返回 false

举个例子:

yield retrun new WaitForSeconds(2f);

等价于实现了一个这样的 MoveNext() 方法:

1
2
3
4
5
6
7
8
private float elapsedTime;  
private float time;

private void MoveNext()
{
elapesedTime += Time.deltaTime;
return time <= elapsedTime;
}

yield 的使用:

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
yield return null / 0 / 任意数字;
// 等待 下一帧 继续执行
yield break;
// 直接结束该协程的后续操作

yield return new WaitUntil()
// 执行直到 当输入的参数(或者委托)为true 时
yield return new WaitWhile()
// 将协同执行直到 当输入的参数(或者委托)不为 true 的时

yield return asyncOperation;
// 等 异步操作结束后 继续执行

yield return WWW();
// 等待 WWW 操作完成后 继续执行

yield return StartCoroution(/*某个协程*/);
// 等待 某个协程执行完毕后 继续执行

yield return new WaitForEndOfFrame();
// 等待 渲染周期循环和GUI完成后 继续执行
yield return new WaitForFixedUpdate();
// 等待 物理周期循环完成后 继续执行

yield return new WaitForSeconds(1.0f);
// 等待 1.0s的时间周期后 继续执行(时间会受到 Time.timeScale 的影响)
yield return new WaitForSecondsRealtime(0.1f);
// 等待 1.0s的时间周期后 继续执行(时间不受 Time.timeScale 的影响)

协程的生命周期

协程虽然是在 MonoBehvaviour 中经过 StartCoroutine 挂起的,但是 协程MonoBehaviour 是一等级的,不受 MonoBehaviour 的状态影响;但协程与 MonoBehaviour 一样受到 GameObject 的控制。

也就是说:在某一个 MonoBehavior 中的协程在执行时,若将该 MonoBehavior .enabled = false,协程并不会停止;但将挂载该 MonoBehaviorGameObject SetActive(false) ,协程就会停止。

20161208161255508

参考

Unity协程(Coroutine)原理深入剖析:https://www.cnblogs.com/123ing/p/3704949.html

Unity协程(Coroutine)原理深入剖析再续:https://www.cnblogs.com/123ing/p/3704947.html

c# yield关键字原理详解:https://www.cnblogs.com/blueberryzzz/p/8678700.html#undefined