协程是Unity3D中的新线程吗?
关于协同程序 (在Unity3D和其他地方)如何工作,我感到困惑和好奇。 coroutine是新线程吗? 他们说Unity的文档 :
协程是一个可以暂停执行(yield)直到给定的YieldInstruction完成的函数。
他们在这里有C#示例:
using UnityEngine; using System.Collections; public class example : MonoBehaviour { void Start() { print("Starting " + Time.time); StartCoroutine(WaitAndPrint(2.0F)); print("Before WaitAndPrint Finishes " + Time.time); } IEnumerator WaitAndPrint(float waitTime) { yield return new WaitForSeconds(waitTime); print("WaitAndPrint " + Time.time); } }
我对这个例子有很多疑问:
-
在上面的例子中,哪一行是协程? 是的
WaitAndPrint()
是一个协程吗? 是WaitForSeconds()
一个协程吗? -
在这一行中:
yield return new WaitForSeconds(waitTime);
,为什么yield
和return
都存在? 我在Unity文档中读到“yield语句是一种特殊的返回,它确保函数将在下次调用yield语句后继续执行。” 如果yield
是一个特殊的return
,那么return
在这里做什么? -
为什么我们必须返回
IEnumerator
? -
StartCoroutine
是否启动了一个新线程? -
在上面的例子中调用了
WaitAndPrint()
多少次?yield return new WaitForSeconds(waitTime);
真的回来了? 如果是,那么我猜WaitAndPrint()
在上面的代码中被调用了两次。 我猜StartCoroutine()
多次调用WaitAndPrint()
。 但是,我看到另一个Unity文档说:“使用yield语句可以在任何时候暂停执行协程。yield return value指定何时恢复协程。” 这些话让我觉得WaitAndPrint()
实际上还没有返回; 它只是暂停了; 它正在等待WaitForSeconds()
返回。 如果是这种情况,那么在上面的代码中,WaitAndPrint()
只被调用一次,而StartCoroutine
只负责启动该函数,而不是多次调用它。
协同程序是一种非常强大的技术,用于模拟.net4.5中async / await支持的各种function,但在早期版本中(c#> = v2.0)。
Microsoft CCR (读一读)也采用(采用?)这种方法。
让我们放开一件事。 单独的yield
无效,并且始终是return
或break
。
想想标准的IEnumerator(它不会产生流控制消息)。
IEnumerator YieldMeSomeStuff() { yield "hello"; Console.WriteLine("foo!"); yield "world"; }
现在:
IEnumerator e = YieldMeSomeStuff(); while(e.MoveNext()) { Console.WriteLine(e.Current); }
什么输出?
你好 FOO! 世界
请注意,在枚举器产生“世界”之前,我们第二次调用MoveNext
,在枚举器中运行了一些代码。 这意味着在Enumerator中,我们可以编写执行代码直到它到达yield return
语句,然后暂停直到有人调用MoveNext
(方便地将所有状态/变量整齐地捕获,所以我们可以从我们中断的地方继续) 。 在MoveNext
调用之后, yield return
语句之后的下一位代码可以运行,直到达到另一个yield return
。 因此,我们现在可以使用对Enumerator的MoveNext
调用来控制yield return
语句之间的代码执行。
现在,我们的枚举器不会产生字符串,而是向MoveNext
的调用者发出一条消息, “请再次调用MoveNext
之前 , 请等待x(waitTime)秒” 。 写入调用者是为了“理解”各种消息。 这些消息将始终遵循“请等待再次调用MoveNext
之前发生此类事件” 。
现在我们有一个强大的暂停和重新启动代码的方法,它需要满足其他条件才能继续,而不必将该function写入另一个方法,比如在没有协同程序的情况下执行异步操作。 如果没有协同程序,你就有力量传递一个可怕的异步状态对象,你需要手动组装以捕获一个方法结束和另一个异步之后的另一个方法的启动之间的状态。 协同程序消除了这一点,因为范围被保留(通过编译器魔术),因此您的本地变量会持久存在于长期存在的异步内容中。
StartCoroutine
只是启动整个过程。 它在Enumerator上调用MoveNext
…一些代码在Enumerator中运行…枚举器产生一条控制消息,它通知StartCoroutine
的代码何时再次调用MoveNext
。 这不需要在新的Thread中发生,但在multithreading场景中可以很方便,因为我们可以从不同的线程调用MoveNext
并控制工作的完成位置。