unity线程与协程
为什么在unity中一般不考虑多线程
在Unity中,使用多线程有较大的限制:
- UnityEngine中的API对象不能在子线程中使用
- UnityEngine中定义的基本数据类型可以使用(如float、int、struct)。但是texture2D不能使用,因为其父类是UnityEngine.Object
多线程的常见使用场景一般只在于:
- 网络通信
- 需要处理复杂算法的计算
- 复杂密集的I/O操作
线程与协程的区别
- 对于协程而言,同一时间只能执行一个协程,而线程是并发的,可以同时有多个线程在运行
- 两者在内存上的使用是相通的,共享堆,不共享栈
最关键的也最简单的区别在于在微观上线程是并行的,而协程是串行的,协程的本质还是单线程,依旧是在主线程中进行。
协程的原理
协程是通过迭代器来实现功能 ,通过关键字IEnumerator来定义一个迭代方法。注意这里使用的是IEnumerator,而不是IEnumerable
这二者间的区别:
- IEnumerator:非泛型的,也是协程认可的参数
- IEnumerable:通过泛型实现的迭代器,协程不使用该迭代器
在迭代器中,最关键的是yield的使用,这是实现协程功能的主要途径,通过该关键方法,可以使得协程的运行暂停、记录下一次的启动时间与位置等。
协程的使用
首先通过一个迭代器定义一个返回值为IEnumerator的方法,然后在程序中通过StartCoroutine来开启一个协程即可。StartCoroutine有多种重载方式:
- StartCoroutine(string methodName):没有参数的情况,可以直接通过方法名来开启协程
- StartCoroutine(IEnumerator routine):通过方法的形式调用
- StartCoroutine(string methodName, object values):带参数的协程通过方法名进行调用
IEnumerator Test(int i) |
在一个协程开始后,同样会对应一个结束协程的方法StopCoroutine与StopAllCoroutines两种方式,但是需要注意的是,两者的使用需要遵循一定的规则。StopCoroutine和StartCoroutine一样有多种重载方法:
- StopCoroutine(string methodName):通过方法名(字符串)来进行
- StopCoroutine(IEnumerator routine):通过方法形式来调用
- StopCoroutine(Coroutine routine):通过指定的协程来关闭
注意:如果使用字符串作为StartCoroutine的参数,在StopCoroutine中也一样需要使用StopCoroutine(string methodName)来结束协程,其他方式同理
在unity中暂停协程(yield)
有几种不同类型的yield语句会暂停协程,在所有情况下,都需要在希望函数被中断的时候使用关键字yield return。
yield标明该方法是一个迭代器,并且将执行超过一帧,而return与常规函数一样,会在该点终止执行并将控制权交还给调用方法。
不同之处在于,使用协程,unity知道从中断的地方继续执行该方法。yield return之后的内容将指定协程在继续之前将停止多长时间。
yield return null
yield return null指示unity等到下一帧再继续
IEnumerator MyCoroutine() |
unity将完成第一个循环,每帧计数一个数字,然后开始第二个循环,每帧倒数一个,直到代码块结束
WaitForSeconds
WaitForSeconds或WaitForSecondsRealtime能指定确切的等待时间,它们只能在协程中使用。
IEnumerator WaitFiveSeconds() |
像这样使用waitforseconds最适合一次性的等待
对于重复延迟,应该先缓存WaitForSeconds对象
WaitForSeconds delay = new WaitForSeconds(1); |
Wait for Seconds Real Time执行完全相同的功能,但使用的是未缩放的时间。
Yield Return WaitUntil / WaitWhile
WaitUntil 暂停执行,直到条件满足,而WaitWhile则是等待条件不满足后再继续
在使用WaitUntil 或WaitWhile代替while循环的好处是更为方便,可以使用lambda表达式在一行代码中检查变量条件
IEnumerator CheckFuel() |
WaitForEndOfFrame
此指令会等到unity渲染完每个camera和UI元素,然后才显示实际帧。一个典型的用途是用于截屏。
IEnumerator TakeScreeshot() |
等待另一个协程
可以让yield直到另一个由yield语句触发的协程完成执行
只需要在yield return之后使用StartCoroutine方法
void Start() |
