Code

2011年3月5日星期六

Coroutine


以前也听说过coroutine这个词,当时以为只是helper function的另外一种说法,故没有去研究个所以然。后来做unity3D project的时候有一次看到这个词,在文档里记录的例子是一个等待的程序,如下:

yield WaitForSeconds(5.0);


这个看起来不就是一个简单的wait吗,用一个loop一个timer就可以实现的一个东西而已。我当时这么认为。unitydoc也不是很详细,所以我也就没再去看。可是后来所UDK的时候遇到了一个叫latent function的东西,类似multi-threading吧,可以独立运行,然后在一段时间后返回。latent function也可以实现像sleep这样的功能。这时候我就想难道这个跟coroutine有什么关系,难道这是一个所以游戏引擎编程共有的功能。


UDK的文档里这样描述latent function:



While an actor is executing a latent function, that actor's state execution doesn't continue until the latent function completes. However, other actors, or the VM, may call functions within the actor. The net result is that all UnrealScript functions can be called at any time, even while latent functions are pending.


In traditional programming terms, UnrealScript acts as if each actor in a level has its own "thread" of execution. Internally, Unreal does not use Windows threads, because that would be very inefficient (Windows 95 and Windows NT do not handle thousands of simultaneous threads efficiently). Instead, UnrealScript simulates threads. 


这样看来我可以把coroutine或者latent function理解成一种模拟更高效的线程。在lua文档里对coroutine解释的很详尽。coroutine的一个关键词是yieldyield相当于一般程序里面的return。区别就在于return是将原来方程的stack context都清除了,下次call这个方程的时候,一个新的stack layer又会建立。而yield把执行权交给calling function同时也save自己的context,下次再被call的时候可以接着之前的context执行。

这样可以模拟多线程。试想有好几个子方程被一个母方程控制在一个loop里面,然后子方程又在自己的loop里面完成一些任务,每loop一次call一次yield。母方程里的loop按顺序resume子方程,这样母方程每loop一次,子方程也一次loop 一次。

coroutine比用thread的好处是coroutine不需要管理多线程会遇到的一些问题,比如说锁。但坏处我觉得是不能做太复杂的东西,因为这样会消耗大量的stack space,而且管理起来会很复杂。其实一些小的任务用coroutine就绰绰有余了,比如说walk()

UDK里的latent function应该是coroutine的一个wrapper。它的功能比unity里的coroutine(貌似只能wait)多了两个,MoveToFinishAnim 在使用这些function的时候我完全可以把它当作一个thread,但它是怎么实现的却是不得而知的。