一、Coroutine(协程)的概念和本质

在网上的一些资料当中,一直将Coroutine当作一个线程来描述,这样是不准确的。因为Coroutine并不是一个新的线程,它仍旧是属于主线程的一部分。Coroutine本质上是一种轻量级的thread,它的开销会比使用thread少很多。多个Coroutine可以按照次序在一个thread里面执行,一个Coroutine如果处于block状态,可以交出执行权,让其他的Coroutine继续执行。

 

而在Unity当中,Coroutine是在所有的Update函数执行完成之后才开始执行的,也就是在LateUpdate()之后执行,主要是用来协助主线程进行工作,就像手动添加了一个另外的update()函数在里面一样,一般情况下我们用于延时触发的功能。下面是关于MonoBehavior的执行顺序图,可以看到Coroutine是在LateUpdate之后的。

                                                          

unity 延迟渲染shader unity延迟调用函数_unity 延迟渲染shader

 

而我们注意到上图中,Coroutine左边标注着几个yield模块,这个就是我们下面要讲的如何使用Coroutine里的内容了。

 

二、Coroutine的使用

在Untiy里面,如果想要将一个函数Fun作为一个Coroutine使用,有两点是需要注意的

1、Fun的返回类型要为IEnumerator

2、yield的使用

 

yield ,在Coroutine里面更像是一个红绿灯的作用,在满足紧跟在它后面的条件之前,这个协程会挂起,把执行权交给调用它的父函数,满足条件时就可以执行yield下面的代码。大家可以执行一下下面的代码看看效果

 

1. using UnityEngine;  
2. using System.Collections;  
3.   
4. public class ExampleClass : MonoBehaviour  
5. {  
6.     IEnumerator WaitAndPrint()  
7.     {  
8. // suspend execution for 5 seconds  
9. return new WaitForSeconds(5);  
10. "WaitAndPrint " + Time.time);  
11.     }  
12.     IEnumerator Start()  
13.     {  
14. "Starting " + Time.time);  
15.   
16. // Start function WaitAndPrint as a coroutine  
17. return StartCoroutine("WaitAndPrint");  
18. "Done " + Time.time);  
19.     }  
20. }

unity 延迟渲染shader unity延迟调用函数_Time_02



另外大家可以再执行一下下面的代码,可以观察一下看看跟上面的有何异同


1. using UnityEngine;  
2. using System.Collections;  
3.   
4. public class ExampleClass : MonoBehaviour  
5. {  
6.     IEnumerator WaitAndPrint()  
7.     {  
8. // suspend execution for 5 seconds  
9. return new WaitForSeconds(5);  
10. "WaitAndPrint " + Time.time);  
11.     }  
12. void Start()  
13.     {  
14. "Starting " + Time.time);  
15.   
16. // Start function WaitAndPrint as a coroutine  
17. "WaitAndPrint");  
18. "Done " + Time.time);  
19.     }  
20. }

unity 延迟渲染shader unity延迟调用函数_Time_03



从上面可以看到,我们是通过StarCoroutine的方法来启动一个新的Coroutine的,在第一段代码当中,把Start方法也声明成一个IEnumerator类型的时候,当执行到yield 模块,就会进行阻塞,等待yield模块完成了之后再继续往下执行,而声名成普通类型的时候,当启动了新的Coroutine的时候,遇到yield了,并不会把整个Start阻塞,而是会记录下当前执行的位置,然后返回父函数中执行,每一帧过后会检测yield是否满足,当满足yield条件,则返回到yield后面执行。

 

在Unity3D中,使用StartCoroutine(string methodName)和StartCoroutine(IEnumerator routine)都可以开启一个线程。区别在于使用字符串作为参数可以开启线程并在线程结束前终止线程,相反使用IEnumerator 作为参数只能等待线程的结束而不能随时终止(除非使用StopAllCoroutines()方法);另外使用字符串作为参数时,开启线程时最多只能传递一个参数,并且性能消耗会更大一点,而使用IEnumerator 作为参数则没有这个限制。

 

在Unity3D中,使用StopCoroutine(string methodName)来终止一个协同程序,使用StopAllCoroutines()来终止所有可以终止的协同程序,但这两个方法都只能终止该MonoBehaviour中的协同程序

还有一种终止Coroutine的方法,将gameObject的active属性设为False,但是设回true的时候并不会启动Coroutine。

注意:将Coroutine所在的脚本设为enable = false并不能够将Coroutine停止,因为它跟Monobehavior是同层级的。