Unity中的coroutine是通过yield expression;来实现的。官方脚本中到处会看到这样的代码。

疑问:

yield是什么?

Coroutine是什么?

unity的coroutine程序执行流程怎么那么奇怪?

unity中的coroutine原理是什么,怎么实现的?

使用unity的coroutine需要注意什么问题?

一、yield的在几种语言中的程序执行特性

Lua中的yield是使得协同函数运行->挂起并且传递参数给resume。resume使得协同函数挂起->运行并且传递参数给协同函数。

C#中yield return/break是用于函数查询集合生成器里面的值(类似迭代)返回,并记录当前现场,下次查询时从上一次记录的yield现场处,继续往下执行,直到继续往下执行没有了,那么退出这段yield的逻辑。yield break会终止掉yield迭代逻辑并跳出。
YieldImplementation:
   1).Caller callsfunction
   2).Caller requestsitem 按需请求一个元素
   3).Next itemreturned 返回请求的元素
   4).Goto step #2

Python中的yield expression, 有yield的函数就变成了一个生成器,调用该函数返回的是迭代器对象,用迭代器对象调用next方法(或者循环中会自动调用next方法),才开始执行函数,执行到yield expression处则中断,返回迭代器当前的值,并保留现场,下次调用next则从现场处开始执行,迭代完了就停止了。可以看出Python中的yield和C#中的yield是类似的,用于创建生成器,执行时中断返回迭代器值,并记录现场,下次从现场处继续执行。

Unity中的yield就是和C#,python中的类似,因为unity是基于.net框架的,且unity脚本开始是用Boo(Python的一个变种)写的。只是unity中多了coroutine特性类型,和StartCoroutine的coroutine管理类。StartCoroutine不是启动了一个新的线程,而是开启一个协同程序,默认unity所有代码都在一个线程中(http://answers.unity3d.com/questions/280597/new-thread-vs-startcoroutine.html)。

coroutine语言层面的原理:

在两年前,协程似乎是一个很高级的东西,随后大多数语言或多或少都支持协程。我比较熟悉的有Python的gevent,Lua的coroutine,Go的goroutine。尤其是Lua和Go,语言本身就支持协程。协程也被叫做轻量级线程。通俗点讲就是定义一大堆任务,然后通过一个线程轮着对每个任务都执行一下,协作运行。它的厉害之处在于每运行到一个任务的时候,它都可以从这个任务上一次中断的地方开始运行。在我们一般的印象中,只有操作系统对线程进行调度的时候才会干这样的事情,进行各种进栈,保存状态。而协程,总共也只是运行在一个线程中,要是使用线程本身的系统栈,早就暴了。因此在这里,实现的时候是用内存来模拟栈的操作。具体实现,我想复杂度一定会不小。

我们知道,线程比进程轻量级,因此产生一个线程消耗的资源比进程少,上下文切换也比进程节约。而协程比线程更加轻量级,上下文切换更是迅速。于是在服务器编程方面给人无限想象。尽管目前还没有出现一款主流的采用协程的web服务器。但是Go语言开发的web服务的性能已经崭露头角了。

二、Unity的Coroutine执行现象

第一种方法:

voidStart()

{

print("Starting " +Time.time);----------------------------------------1

StartCoroutine(WaitAndPrint(2));-------------------------------------2

print("Done " +Time.time);-------------------------------------------3

}

IEnumerator WaitAndPrint(float waitTime)

{

yield return new WaitForSeconds(waitTime);------------------------4

print("WaitAndPrint " + Time.time);----------------------------------5

}

该段代码的执行顺序是12435

IEnumerator Start()

{

print("Starting " +Time.time);----------------------------------------1

yield return StartCoroutine(WaitAndPrint(2.0F));------------------------2

print("Done " +Time.time);------------------------------------------3

}

IEnumerator WaitAndPrint(float waitTime)

{

yield return new WaitForSeconds(waitTime);----------------------------4

print("WaitAndPrint " + Time.time);-----------------------------------------5

}

该段代码的执行顺序是12453

Why?这么奇怪的执行方式。

三、Unity官方文档对coroutine的解释

Normal coroutine updates are run after theUpdate function returns. Acoroutine is a function that can suspend its execution (yield) until the givenYieldInstruction finishes. Different uses of Coroutines:

yield; The coroutine will continue after all Update functionshave been calledon the next frame.

yield WaitForSeconds(2); Continueafter a specified time delay, after all Update functions have been called for theframe.

yield WaitForFixedUpdate(); Continue afterall FixedUpdate has been called on all scripts.

yield WWWContinue aftera WWW download has completed

yield StartCoroutine(MyFunc); Chains the coroutine, and will wait for the MyFunc coroutine to completefirst.

C#要在yield coroutine之间加上return关键字。

四、Unity中的Coroutine原理猜测

     虚拟机分段执行机制, 同类型嵌套用栈存放实现串行执行:.NET虚拟机在每一帧循环中, 会依次进入每个编译器预定义好的入口。对于Coroutine类型,编译器需要产生一些代码,在Coroutine类型指定的时间或事件完成后(.net的虚拟机用函数指针进行标记管理现场和在流程中每帧检查时间或者事件满足后发送消息,将cpu所有权交给yield中断的现场,或是通过包含不同Coroutine迭代器的多个管理类管理各个coroutine, 每帧用coroutine子类通过多态检查时间或事件到达,将cpu所有权交给coroutine子类中断的现场),从yield中断后的代码处继续往下执行, 这样就形成了我们看到的一个function能分段执行的机制。

而对于嵌套Coroutine类型,会串行的执行而不是并行的,可能.net虚拟机对于同coroutine类型用栈存放,栈顶的先执行,从而实现串行执行,如果外层的不使用yield return,那么不会串行执行,而是并行执行。于是就可以解释上面例子中的执行次序问题。

原理图:

见:http://www.richardfine.co.uk/2012/10/unity3d-monobehaviour-lifecycle/

五、Unity中使用Coroutine需要注意的问题:

1.使用的地方和不能使用的地方:

必须在MonoBehaviour或继承于MonoBehaviour的类中调用 yield coroutine。yield不可以在Update或者FixedUpdate里使用。

2.开启协程:

StartCoroutine(string methodName)和StartCoroutine(IEnumeratorroutine)都可以开启一个协程,

区别:

使用字符串作为参数时,开启协程时最多只能传递一个参数,并且性能消耗会更大一点; 而使用IEnumerator 作为参数则没有这个限制。

3.删除协程:

1).在Unity3D中,使用StopCoroutine(stringmethodName)来终止该MonoBehaviour指定方法名的一个协同程序,使用StopAllCoroutines()来终止所有该MonoBehaviour可以终止的协同程序。

包括StartCoroutine(IEnumerator routine)的。

2).还有一种方法可以终止协同程序,即将协同程序所在gameobject的active属性设置为false,当再次设置active为ture时,协同程序并不会再开启;

如是将协同程序所在脚本的enabled设置为false则不会生效。

4.js和C#中使用区别:

在C#中要使用 yield return而不是yield。

C#中yield(中断)语句必须要在IEnumerator类型里,C#方法的返回类型为IEnumerator,返回值如(eg:yield return new WaitForSeconds(2); 或者 yield returnnull);

5.协程函数返回值和参数类型,组合的设计模式:

协同程序的返回类型为Coroutine类型。在Unity3D中,Coroutine类继承于YieldInstruction,所以,协同程序的返回类型只能为null、等待的帧数(frame)以及等待的时间。

协同程序的参数不能指定ref、out参数。但是,我们在使用WWW类时会经常使用到协同程序,由于在协同程序中不能传递参数地址(引用),也不能输出对象,

这使得每下载一个WWW对象都得重写一个协同程序,解决这个问题的方法是建立一个基于WWW的类(用组合模式来解决),并实现一个下载方法。如下:

using UnityEngine;

using System.Collections;

public class WWWObject : MonoBehaviour

{

public WWW www;

public WWWObject(string url)

{

if(GameVar.wwwCache)

www = WWW.LoadFromCacheOrDownload(url, GameVar.version);

else

www = new WWW(url);

}

public IEnumerator Load()

{

Debug.Log("Start loading : " + www.url);

while(!www.isDone)

{

if(GameVar.gameState == GameState.Jumping || GameVar.gameState ==GameState.JumpingAsync)

LoadScene.progress = www.progress;

yield return 1;

}

if(www.error != null)

Debug.LogError("Loading error : " + www.url + "\n" +www.error);

else

Debug.Log("End loading : " + www.url);

}

public IEnumerator LoadWithTip(string resourcesName)

{

Debug.Log("Start loading : " + www.url);

LoadScene.tipStr = "Downloading  resources<" + resourcesName + "> . . .";

while(!www.isDone)

{

if(GameVar.gameState == GameState.Jumping || GameVar.gameState ==GameState.JumpingAsync)

LoadScene.progress= www.progress;

yield return 1;

}

if(www.error != null)

Debug.LogError("Loading error : " + www.url + "\n" +www.error);

else

Debug.Log("End loading : " + www.url);

}

}

调用:

using UnityEngine;

using System.Collections;

using System.Collections.Generic;

public class LoadResources : MonoBehaviour

{

static string url ="http://61.149.211.88/Package/test.unity3d";

public static WWW www = null;

IEnumerator Start()

{

if(!GameVar.resourcesLoaded)

{

GameVar.gameState = GameState.Jumping;

WWWObject obj = new WWWObject(url);

www = obj.www;

yield return StartCoroutine(obj.LoadWithTip("Textures"));

GameVar.resourcesLoaded = true;

GameVar.gameState = GameState.Run;

}

}

}

参考文章:

http://game.ceeger.com/forum/read.php?tid=13148

http://www.zhihu.com/question/23895384

http://blog.csdn.net/tkokof1/article/details/11842673

http://blog.csdn.net/tkokof1/article/details/12834939

http://www.richardfine.co.uk/2012/10/unity3d-monobehaviour-lifecycle/

http://dsqiu.iteye.com/blog/2029701

Unity3D中的Coroutine详解的更多相关文章

  1. 【Unity3D/C#】Unity3D中的Coroutine详解

    Unity中的coroutine是通过yield expression;来实现的.官方脚本中到处会看到这样的代码. 疑问: yield是什么? Coroutine是什么? unity的coroutin ...

  2. Tornado中gen.coroutine详解

    1.gen.coroutine的作用 自动执行生成器 2.Future对象 在介绍异步使用之前,先了解一下Future对象的作用. Future简单可以理解为一个占位符,将来会执行的对象,类似java ...

  3. php中关于引用(&)详解

    php中关于引用(&)详解 php的引用(就是在变量或者函数.对象等前面加上&符号) 在PHP 中引用的意思是:不同的变量名访问同一个变量内容. 与C语言中的指针是有差别的.C语言中的 ...

  4. JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解

    二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...

  5. AngularJS select中ngOptions用法详解

    AngularJS select中ngOptions用法详解   一.用法 ngOption针对不同类型的数据源有不同的用法,主要体现在数组和对象上. 数组: label for value in a ...

  6. 【转载】C/C++中extern关键字详解

    1 基本解释:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义.此外extern也可用来进行链接指定. 也就是说extern ...

  7. oracle中imp命令详解 .

    转自http://www.cnblogs.com/songdavid/articles/2435439.html oracle中imp命令详解 Oracle的导入实用程序(Import utility ...

  8. Android中Service(服务)详解

    http://blog.csdn.net/ryantang03/article/details/7770939 Android中Service(服务)详解 标签: serviceandroidappl ...

  9. python中threading模块详解(一)

    python中threading模块详解(一) 来源 http://blog.chinaunix.net/uid-27571599-id-3484048.html threading提供了一个比thr ...

随机推荐

  1. Lucene IndexReader,IndexWriter,IndexSearcher 缓存应用

    1.IndexManager类,用于提供IndexReader,IndexWriter,IndexSearcher获取接口 import java.io.File; import java.io.IO ...

  2. PHP面向对象之旅:模板模式(转)

    抽象类的应用就是典型的模版模式 抽象类的应用就是典型的模版模式,先声明一个不能被实例化的模版,在子类中去依照模版实现具体的应用. 我们写这样一个应用: 银行计算利息,都是利率乘以本金和存款时间,但各种 ...

  3. my.cnf已经存在,影响安装--mysql

    Found existing config file ./my.cnf on the system. Because this file might be in use, it was not rep ...

  4. 5 - SQL Server 2008 之 四则运算、比较运算、逻辑运算及字符连接运算

    四则运算如下: --加减乘除(+.-.*.\.%)取余运算 SELECT --加法运算 AS 加法结果2, --减法运算 -2.5 AS 减法结果1, 15.5+5.5 AS 减法结果2, --乘法运 ...

  5. asp.net下载文件的几种方法

    最近做东西遇到了下载相关的问题.在这里总结一下自己处理的方法. 1.以字节流的形式向页面输出数据以下载Excel为例子. string path=Server.MapPath("文件路径&q ...

  6. 用CSS+Jquery实现一个漂浮的窗体

    一.项目需求: 实现一个用于网站主页面 从窗体左上角开始飘到右下角 之后又飘到右上角 十秒之后消失的效果. 二.需求分析: 首先 应当想要漂浮的内容放在一个容器内,也就是一个DIV,设计它的样式,不管 ...

  7. Spring通过SchedulerFactoryBean实现调度任务的配置

    http://blog.csdn.net/hu_shengyang/article/details/19815201(里面是配置) 介绍SchedulerFactoryBean http://blog ...

  8. Android布局管理器(表格布局)

    表格布局有TableLayout所代表,TableLayout继承了LinearLayout,因此他的本质依然是LinearLayout. 表格布局采用行.列的形式来进行管理,在使用的时候不需要声明多 ...

  9. CI 笔记(1)

    1. 下载CI,官方网站,目前3.x版本已经更新,2.2.6版本为2.x版本的最后的一个版本.为了和视频教材一致,使用CI 2.x版本 2. 目录结构,从application里面的,controll ...

  10. kill session真的能杀掉进程吗

    session1 确认sidSYS @ prod > select userenv('sid') from dual; USERENV('SID')-------------- 144 sess ...