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 在每帧做的工作就是:调用 IEnumerator 的 MoveNext() 方法,如果返回 true ,就从当前位置继续往下执行;如果是返回 false,就是结束迭代器。

Yield

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

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

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

当这个 IEnumerator 对象的 MoveNext() 返回 false 时(也就是说这个 IEnumertator 的 Current 已经迭代到最后一个元素),Unity 就会将这个 IEnumerator 从 Cortoutines 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,协程并不会停止;但将挂载该 MonoBehavior 的 GameObject 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