unity协程理解
什么是协程?
unity协程是一个能够暂停协程执行,暂停后立即返回主函数,执行主函数剩余的部分,直到中断指令完成后,从中断指令的下一行继续执行协程剩余的函数。函数体全部执行完成,协程结束。
由于中断指令的出现,使得可以将一个函数分割到多个帧里去执行。
协程的好处:
让原来要使用异步 + 回调方式写的非人类代码, 可以用看似同步的方式写出来。
能够分步做一个比较耗时的事情,如果需要大量的计算,将计算放到一个随时间进行的协程来处理,能分散计算压力
协程的坏处:
协程本质是迭代器,且是基于unity生命周期的,大量开启协程会引起gc
如果同时激活的协程较多,就可能会出现多个高开销的协程挤在同一帧执行导致的卡帧
协程书写时的性能优化:
常见的问题是直接new 一个中断指令,带来不必要的 GC 负担,可以复用一个全局的中断指令对象,优化掉开销。
协程是在什么地方执行?
协程是通过迭代器来实现功能的,在迭代器中,最关键的是yield 的使用,这是实现协程功能的主要途径,通过该关键方法,可以使得协程的运行暂停、记录下一次启动的时间与位置。
yield 也是脚本生命周期的一些执行方法,不同的yield 的方法处于生命周期的不同位置:
yield return null; 暂停协程等待下一帧继续执行
yield return 0或其他数字; 暂停协程等待下一帧继续执行
yield return new WairForSeconds(时间); 等待规定时间后继续执行
yield return StartCoroutine("协程方法名");开启一个协程(嵌套协程)
yield return GameObject; 当游戏对象被获取到之后执行
yield return new WaitForFixedUpdate():等到下一个固定帧数更新
yield return new WaitForEndOfFrame():等到所有相机画面被渲染完毕后更新
yield break; 跳出协程对应方法,其后面的代码不会被执行
yield return new WaitUntil(...)根据定义挂起语句, 直到指定的条件返回 true
yield return new WaitWhile(...)根据定义挂起语句, 直到指定的条件返回 false
协程怎么开始?
首先通过一个迭代器定义一个返回值为IEnumerator的方法,然后再程序中通过StartCoroutine来开启一个协程即可:
在正式开始代码之前,需要了解StartCoroutine的两种重载方式:
StartCoroutine(string methodName):这种是没有参数的情况,直接通过方法名(字符串形式)来开启协程
StartCoroutine(IEnumerator routine):通过方法形式调用,方法可以带参数
StartCoroutine(string methodName,object values):带参数的通过方法名(字符串形式)+参数(参数类型而不是字符串)进行调用
协程怎么结束?
StopCoroutine(string methodName):通过方法名(字符串)来进行
注意事项:如果使用StartCoroutine(string methodName)来开启一个协程的,那么结束协程就只能使用StopCoroutine(string methodName)和StopCoroutine(Coroutine routine)来结束协程
StopCoroutine(IEnumerator routine):通过方法形式来调用
StopCoroutine(Coroutine routine):通过指定的协程来关闭
StopAllCoroutines暂停的是当前脚本下的所有协程
gameObject.active = false 可以停止该对象上全部协程的执行,即使再次激活,也不能继续执行。但注意MonoBehaviour enabled = false 不能停止协程;对比 update却是可以在MonoBehaviour enabled = false 就中止
原因:由于协程在StartCoroutine时被注册到的GameObject上,他的生命期受限于GameObject的生命期,因此受GameObject是否active的影响。
结论:协程虽然是在MonoBehvaviour启动的(StartCoroutine)但是协程函数的地位完全是跟MonoBehaviour是一个层次的,不受MonoBehaviour的状态影响。
协程结束的标志是什么?
如果最后一个 yield return 的 IEnumerator 已经迭代到最后一个是,MoveNext 就会 返回 false 。这时,Unity就会将这个 IEnumerator 从 cortoutines list 中移除。
只有当这个对象的 MoveNext() 返回 false 时,即该 IEnumertator 的 Current 已经迭代到最后一个元素了,才会执行 yield return 后面的语句。
协程的使用场景例子:
将一个复杂程序分帧执行;
制作计时器;
异步加载等功能,比如:
AB包资源的异步加载
Reaources资源的异步加载
场景的异步加载
WWW模块的异步请求
线程、进程、协程的区别:
进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。
线程拥有自己独立的栈和共享的堆,只共享堆,不共享栈,线程亦由操作系统调度(标准线程是的)。
协程和线程一样只共享堆,不共享栈,协程由程序员在协程的代码里显示调度。
一个应用程序一般对应一个进程,一个进程一般有一个主线程,还有若干个辅助线程,线程之间是并行的,在线程里面可以开启协程,协程是串行的,一个脚本中可以有多个协程,但同一时刻只有一个协程在运行。
协程和线程的区别是:协程避免了无意义的调度,由此可以提高性能,但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力。