Unity Coroutine详解(一)
Unity 中协程是个非常强大的功能,其作用主要是用于游戏中的延时调用或者执行一连串的有时间间隔的事件流程,例如剧情对话等。简单总结了几点协程相关的知识点,旨在加深记忆,同时为初学者解惑。
1、协程、进程与线程
这是个面试中经常会问到的问题:协程、进程与线程的区别在哪?
说到协程,我们首先回顾以下线程与进程这两个概念。在操作系统(os)级别,有进程(process)和线程(thread)两个我们看不到但又实际存在的“东西”,这两个东西都是用来模拟“并行”的,写操作系统的程序员通过用一定的策略给不同的进程和线程分配CPU计算资源,来让用户“以为”几个不同的事情在“同时”进行“。在单CPU上,是os代码强制把一个进程或者线程挂起,换成另外一个来计算,所以,实际上是串行的,只是“概念上的并行”。在现在的多核的cpu上,线程可能是“真正并行的”。
进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。
线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(标准线程是的)。
协程和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。
一个应用程序一般对应一个进程,一个进程一般有一个主线程,还有若干个辅助线程,线程之间是平行运行的,在线程里面可以开启协程,让程序在特定的时间内运行。
协程和线程的区别是:协程避免了无意义的调度,由此可以提高性能,但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力。
打个比方吧,假设有一个操作系统,是单核的,系统上没有其他的程序需要运行,有两个线程 A 和 B ,A 和 B 在单独运行时都需要 10 秒来完成自己的任务,而且任务都是运算操作,A B 之间也没有竞争和共享数据的问题。现在 A B 两个线程并行,操作系统会不停的在 A B 两个线程之间切换,达到一种伪并行的效果,假设切换的频率是每秒一次,切换的成本是 0.1 秒(主要是栈切换),总共需要 20 + 19 * 0.1 = 21.9 秒。如果使用协程的方式,可以先运行协程 A ,A 结束的时候让位给协程 B ,只发生一次切换,总时间是 20 + 1 * 0.1 = 20.1 秒。如果系统是双核的,而且线程是标准线程,那么 A B 两个线程就可以真并行,总时间只需要 10 秒,而协程的方案仍然需要 20.1 秒。
其实就根本来说,协程除了名字之外,和线程是没有太大联系的。Unity中的特殊在于所有的脚本和代码都是在一个主线程里运行的,协程也不例外。协程与线程的相似点只在于,协程看起来也可以与其他函数并行执行。 但本质上来说,线程是通过可以开启多个子线程同时执行程序,而达到并行。而协程则是通过每帧检测的方式,在自己与其他函数之间切换。这种“来回跑”的方式,与Unity中一惯有明确执行顺序的风格(脚本生命周期)看起来不太统一。但这正是它的强大之处,使得我们在使用协程的时候不必考虑lock等诸多线程中的问题。
2、协程执行原理
unity中协程执行过程中,通过yield return XXX,将程序挂起,去执行接下来的内容,注意协程不是线程,在为遇到yield return XXX语句之前,协程额方法和一般的方法是相同的,也就是程序在执行到yield return XXX语句之后,接着才会执行的是 StartCoroutine()方法之后的程序,走的还是单线程模式,仅仅是将yield return XXX语句之后的内容暂时挂起,等到特定的时间才执行。
那么挂起的程序什么时候才执行,这就要看monoBehavior的生命周期了。
也就是协同程序主要是update()方法之后,lateUpdate()方法之前调用的,接下来我们通过一个小例子去理解一下。
using UnityEngine;
using System.Collections;
using System.Threading;
public class test : MonoBehaviour
{ void Start()
{
StartCoroutine(tt());//开启协程
for (int i = ; i < ; i++) //循环A
{
Debug.Log("*************************" + i);
Thread.Sleep();
}
} IEnumerator tt()
{
for (int i = ; i < ; i++) //循环B
{
Debug.Log("-------------------" + i);
} yield return new WaitForSeconds(); //协程1 for (int i = ; i < ; i++) //循环C
{
Debug.Log(">>>>>>>>>>>>>>>>>>>>" + i);
yield return null; //协程1
}
} // 更新数据
void Update()
{
Debug.Log("Update");
} //晚于更新
void LateUpdate()
{
Debug.Log("------LateUpdate");
} }
程序的运行结果为:
先执行循环B,然后执行循环A,然后执行update()和lateUpdate()的方法,等待1S之后,在updat()和lateupda()之间执行循环C的输出。
3、yield return 的不同返回类型
使用yield return的时候你会发现它可以返回的类型一长串,对于初学者我觉得就分为带 new和不带new的就行了。
先说不带new的。通常可以yield return的有 null,数字 ,字符串,布尔值甚至表达式,函数,嵌套协程等。
以在Start()中开启当前协程为例,如果是不带new的返回类型,执行时间都是一样的。即在第一时间执行协程中的代码 到第一个yield return当行为止,然后在下一帧的Update之后,LateUpdate之前执行yield return后面的代码。
另外需要注意的是,yield return后面可以是一个函数调用,赋值表达式,嵌套的其它协程等。以赋值的表达式num=10为例;它会在当行yield return执行的时候就执行,函数调用和其它协程也是一样。也就是说,此时yield return的函数调用就相当于直接调用了这个函数,并且是当时就执行的。 而其它return 类型 如null,字符串,数字等一般只用作延迟一帧来用,其它作用,待我后期再研究下。
下面说带new的,也是通常我们重点使用的协程功能。
这里列举几个:
(1)new WaitUntil(Func<bool>) 参数是一个布尔返回类型的委托,作用是,知道这个返回的布尔值为true时,协程才会继续执行当行yield return 后面的代码。
(2) new WaitForSeconds(float)参数是float类型的数字,表示秒,也是协程最常用的功能之一。 作用是,在N秒后才会继续执行当行yield return 后面的代码。
由于yield return可以在一个协程中任意位置写多个,配合这个可以实现很多时间细化可视化的功能。
(3)new WaitForEndOfFrame()作用是,在结束当前帧 摄像机和GUI被渲染以及其它函数完成后才会继续执行当行yield return 后面的代码。 这个我只验证了在LateUpdate执行完之后执行,具体在整个脚本周期中哪个函数执行完之后开始执行还未详细验证。
(4)new WaitForFixedUpdate() 作用是,直到当行代码之后第一个FixedUpdate执行之后才会继续执行当行yield return 后面的代码。也就是说,如果是在start里面开启协程的话,第一次执行FixedUpdate之后就会继续执行return后面的代码。
后面还有许多类型的 返回,没有一一验证,不过作用应该大同小异,即在执行第一个该类型动作之后才会继续执行当行yield return 后面的代码。
值得一提的是,协程的延迟调用和非阻塞式挂起是用于网络请求等高级结构很好的工具,非常值得花一些时间去仔细研究。
4、开始协程
通过MonoBehaviour提供的StartCoroutine方法来实现启动协同程序。
1、StartCoroutine(IEnumerator routine);
优点:灵活,性能开销小。
缺点:无法单独的停止这个协程,如果需要停止这个协程只能等待协同程序运行完毕或则使用StopAllCoroutine();方法。
2、StartCoroutine (methodName:string, value : object = null);
优点:可以直接通过传入协同程序的方法名来停止这个协程:StopCoroutine(string methodName);
缺点:性能的开销较大,只能传递一个参数。
5、停止协程
协程内停止可以用yield return break;
协程外:
1、StopCoroutine(string methodName);
2、StopAllCoroutine();
3、设置gameobject的active为false时可以终止协同程序,但是再次设置为true后协程不会再启动。设置当前协程所在脚本enable为false也并不能停止当前协程的执行。
Unity Coroutine详解(一)的更多相关文章
- Unity Coroutine详解(二)
• 介绍• Part 1. 同步等待• Part 2. 异步协程• Part 3. 同步协程• Part 4. 并行协程 1.介绍 ...
- Unity Jobsystem 详解实体组件系统ECS
原文摘选自Unity Jobsystem 详解实体组件系统ECS 简介 随着ECS的加入,Unity基本上改变了软件开发方面的大部分方法.ECS的加入预示着OOP方法的结束.随着实体组件系统ECS的到 ...
- Unity 灯光系统详解
Unity 灯光系统详解 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分享.心 ...
- Lua Coroutine详解
协同程序与线程差不多,也就是一条执行序列,拥有自己独立的栈,局部变量和指令指针,同时又与其它协同程序共享全局变量和其它大部分东西.线程与协同程序的主要区别在于,一个具有多线程的程序可以同时运行几个线程 ...
- Unity3D中的Coroutine详解
Unity中的coroutine是通过yield expression;来实现的.官方脚本中到处会看到这样的代码. 疑问: yield是什么? Coroutine是什么? unity的coroutin ...
- 【Unity3D/C#】Unity3D中的Coroutine详解
Unity中的coroutine是通过yield expression;来实现的.官方脚本中到处会看到这样的代码. 疑问: yield是什么? Coroutine是什么? unity的coroutin ...
- Tornado中gen.coroutine详解
1.gen.coroutine的作用 自动执行生成器 2.Future对象 在介绍异步使用之前,先了解一下Future对象的作用. Future简单可以理解为一个占位符,将来会执行的对象,类似java ...
- Unity SurfaceShader详解
声明:文章主要是总结手游开发的经验,只涉及到了前向渲染.未涉及延迟渲染. Unity的Surface Shader本质上就是VS/PS.只不过Unity经过精心设计,将shader划分为了几个关键部分 ...
- Unity灯光详解
Lights will bring personality and flavor to your game. You use lights to illuminate the scenes and o ...
随机推荐
- C++11智能指针(unique_ptr、shared_ptr、weak_ptr)(转)
原文地址:https://blog.csdn.net/king_way/article/details/95536938
- c++ md5算法实现(转)
原文链接:C++计算文件的MD5值 其他:c++输入文件流ifstream用法详解
- js异步执行 按需加载 三种方式
js异步执行 按需加载 三种方式 第一种:函数引用 将所需加载方法放在匿名函数中传入 //第一种 函数引用 function loadScript(url,callback){ //创建一个js va ...
- Android_几种菜单
Android中的菜单有如下几种: OptionMenu:选项菜单,android中最常见的菜单,通过Menu键来调用 SubMenu:子菜单,android中点击子菜单将弹出一个显示子菜单项的悬浮框 ...
- IT人的乐趣与价值
it人员“偷摸”实现个人潜在价值的一些方向. 1.做一名站长.现在做一个个人博客或者CMS系统,都可以从网上找到相关开源的程序.花十几块钱申请个域名,再花个百来块租个空间,你就具备了当站长的外界 ...
- 【转载】深入理解Java虚拟机笔记---运行时栈帧结构
栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区的虚拟机栈(Virtual Machine Stack)的栈元素.栈帧存储了方法的局部变量表,操作 ...
- SpringBoot学习- 5、整合Redis
SpringBoot学习足迹 SpringBoot项目中访问Redis主要有两种方式:JedisPool和RedisTemplate,本文使用JedisPool 1.pom.xml添加dependen ...
- mysql逻辑备份与还原工具mysqldump
(一)mysqldump介绍 mysqldump是MySQL自带的逻辑备份工具,类似于Oracle的expdp/impdp,mysqldump备份十分灵活,可以在以下级别对数据库进行备份: 实例下的所 ...
- 常用UrlEncode编码结果
空格 ! # $ % + @ : = ? %20 %21 %23 %24 %25 %2B %40 %3A %3D %3F
- ApiBehaviorOptions 统一模型验证配置不生效
ApiBehaviorOptions 的统一模型验证配置一定要放到(.AddMvc)后面.