Coroutine协程函数
本质上是一个返回类型IEnumerator声明的函数,并且yield return语句包含在函数的某个地方。yield return语句,会执行暂停,并在下一帧从暂停处开始继续执行。
对复杂逻辑进行分帧,可以在主线程内非堵塞的运行一个持续性的逻辑。
实现原理:
C#编译器会帮我们创建一个协程的类,而在开启一个协程时就会创建对应的对象,这个对象用来维护多次调用时协程的状态。正是要维护这些状态,所以协程内的本地变量也需要放到堆上,启动一个Coroutine所引起的内存消耗等同于一个类的固定成本加上这个 Coroutine所用到的局部变量总内存。而协程的生命周期就是跟着MonoBehaviour来走的。
常用几种Coroutine操作:
//会在下一帧开始的时候继续执行
yield return null;
//会在下一帧结束的时候继续执行
yield return new WaitForEndOfFrame();
yield return new WaitForFixedUpdate();
yield return new WaitForSeconds(2.0f);
//等待WWW返回
WWW www = new WWW("http://www.baidu.com");
yield return www;
//等待另一个协程,这是把协程串联起来的关键
yield return StartCoroutine(WaitAndPrint(0.5f));
执行效率
一个什么都不做的协程,至少需要两帧。
某些条件下,可以写一套非协程代码,一套协程代码,如果数据在缓存中存在,直接执行非协程代码,否则执行协程代码,访问网络。减少无用的多帧操作。
实际应用
1、实现定时器
2、利用协程,将复杂操作分帧计算,在协程内部统计一个数,当这个数大于一个值,清0并且yield return null,在下一帧开始的时候继续执行,不会堵塞主线程。
3、通过下面这段逻辑,在协程中实现控制互斥区域
//这里要引起注意的是,第一次写成if(m_DownLoading),
//因为同时执行协程,m_DownLoading还没有被设置为true就达不到这样的效果了
while (m_DownLoading)
{
yield return null;
}
m_DownLoading = true;
4.等待另一个协程,把协程串联起来
IEnumerator Work()
{
yield return StartCoroutine(NavToPos(start.localPosition));
yield return StartCoroutine(Jump());
yield return StartCoroutine(NavToPos(end.localPosition));
}
常见使用误区
- 协程是在主线程
- 必须依赖于一个启动它的mono,当协程程序所在脚本的active属性设置为false时,协程不会停止。
- 当协程程序所在的gameobject的active属性设置为false时,协程停止。再次设置active属性为true,协程不会再启动。
- 当协同程序所在脚本或gameobject被destroy时,协程会被终止。
- 使用MonoBehaviour.StopCoroutine和MonoBehaviour.StopAllCoroutines来停止Coroutine。