首先,请你牢记:协程不是线程,也不是异步执行的。协程和 MonoBehaviour 的 Update函数一样也是在MainThread中执行的。使用协程你不用考虑同步和锁的问题。
UnityGems.com给出了协程的定义:
A coroutine is a function that is executed partially and, presuming suitable conditions are met, will be resumed at some point in the future until its work is done.
协程是一个分部执行,遇到条件(yield return 语句)会挂起,直到条件满足才会被唤醒继续执行后面的代码。Unity在每一帧(Frame)都会去处理对象上的协程。Unity主要是在Update后去处理协程(检查协程的条件是否满足):
从上图的剖析就明白,协程跟Update()其实一样的,都是Unity每帧对会去处理的函数(如果有的话)。如果MonoBehaviour 是处于激活(active)状态的而且yield的条件满足,就会协程方法的后面代码。还可以发现:如果在一个对象的前期调用协程,协程会立即运行到第一个 yield return 语句处,如果是 yield return null ,就会在同一帧再次被唤醒。如果没有考虑这个细节就会出现一些奇怪的问题。
注:图和结论都是从UnityGems.com 上得来的,经过验证发现与实际不符,D.S.Qiu用的是Unity 4.3.4f1 进行测试的。经过测试验证,协程至少是每帧的LateUpdate()后去运行。
协程其实就是一个IEnumerator(迭代器),IEnumerator 接口有两个方法 Current 和 MoveNext() ,迭代器方法运行到 yield return 语句时,会返回一个expression表达式并保留当前在代码中的位置。 当下次调用迭代器函数时执行从该位置重新启动。unity3d在每帧做的工作就是:调用协程(迭代器)MoveNext() 方法,如果返回 true ,就从当前位置继续往下执行。详情见这篇博客。
如果理解了这张图,之前显示人物对话的功能最后提到的那些疑惑也就很容易理解了:
- 协程和Update()一样更新,自然可以使用Time.deltaTime了,而且这个Time.deltaTime和在Update()当中使用是一样的效果(使用yield return null的情况下)
- 协程并不是多线程,它和Update()一样是在主线程中执行的,所以不需要处理线程的同步与互斥问题
- yield return null其实没什么神奇的,只是unity3d封装以后,这个协程在下一帧就被自动调用了
- 可以理解为ShowDialog()构造了一个自己的Update(),因为yield return null让这个函数每帧都被调用了