Unity C#笔记 协程详解(转)

目录

  • 什么是协程
  • 多线程
  • 协程
    • 协程的使用场景
    • 协程使用示例
    • Invoke的缺陷
  • 协程语法
    • 开启协程
    • 终止协程
    • 挂起
  • 协程的执行原理

什么是协程

在Unity中,协程(Coroutines)的形式是我最喜欢的功能之一,我都会使用它来控制需要定时的。

协同程序,在主程序运行的同时,开启另外一段逻辑处理,来协同当前程序的执行。
可能看了这段文字介绍还是有点模糊,其实可以用多线程来比较。

多线程

多线程,顾名思义,多条同时执行的线程。
最初,多线程的诞生是为了解决IO阻塞问题,如今多线程可以解决许多同样需要异步方法的问题(例如网络等)。
所谓异步,通俗点讲,就是我走我的线程,你走你的线程。当某个线程阻塞时,另一个线程不会受影响继续执行。

需要认识到的是,多线程并不是真正意义上的多条线程同时执行。
它的实际是将一个时间段分成若干个时间片,每个线程轮流运行一个时间片。

(如图,将执行步骤切分成极小的粒度,然后依次运行)

但是由于时间片粒度非常非常小,几乎看不出区别,所以程序执行效果跟真正意义上的并行执行效果基本一致。

多线程的缺陷

然而多线程有一个坏处,就是可能造成共享数据的冲突。

假如有一个变量i = 0, Step1_1的操作是进行++i操作,Step2_1的操作是进行--i操作。
我们预期最终结果i为0。

但由于操作切分得过小,可能会发生这样顺序的事:

  • 线程1:访问i, 将0存到寄存器
  • 线程2:访问i, 将0存到寄存器
  • 线程1:++i, 得到1
  • 线程2:--i, 得到-1
  • 线程1:将1写入到i的内存
  • 线程2:将-1写入到i的内存
  • 最终i的值为-1

当然多线程的冲突也有解决方案: 互斥锁....

但是这些多多少少会付出额外的代价,让程序变得臃肿。

协程

CPU有多条线程,一条线程可以有多个协程。

协程跟多线程类似,也有类似异步的效果(注意不是真正的异步)。
只不过它的切分粒度不是基于系统划分的时间片,而是基于我们编写的yield,而且往往粒度更大。

粒度是取决于自己定义什么时候让协程挂起:

//下面定义了一个协程函数,注意必须使用IEnumerator作为返还值才能成为协程函数。
IEnumerator Test() { for(int i = 0; i<1000 ; ++i){ ans += i; yield return 0;//挂起,下一帧再来从这个位置继续执行。 } j+=2; yield return 0;//挂起,下一帧再来从这个位置继续执行。 ++j; yield return 0;//挂起,下一帧再来从这个位置继续执行。 }

如果划分的粒度过大,协程所在的线程可能在相应的帧卡顿。
甚至如果让协程阻塞(死循环),那么协程所在的整个线程也会阻塞。
因此说协程可以有类似异步的效果,但是不是真正的异步。

协程的一大好处就是可以避免数据访问冲突的问题:
因为它的粒度相对多线程的大很多,所以往往很少出现冲突现象

在上面多线程的例子里,使用协程则可以这样:

  • Step1_1: 执行完++i, 此时i=1
  • Step2_1: 执行完--i, 此时i=0
  • 最终i的值为0

协程的使用场景

对于保证不会阻塞的并行操作且并行性要求不高的并行操作,可以使用协程。
更实际来说,协程最常用于延时执行等控制时间轴的操作,例如N秒后调用指定函数。

利用每帧执行一段协程的特性,我们可以引入个带累加计时判断循环,然后再超过3秒后跳出循环,执行Debug.Log()

//3s后执行Debug.Log
IEnumerator Test() { for(float timer = 0.0f; timer < 3.0f ; timer += Time.DeltaTime){ yield return 0;//挂起,下一帧再来从这个位置继续执行。 } Debug.Log("启动协程3s后"); }

但是Unity封装了个更好用的类:WaitForSeconds
使这种延时的协程代码更加简洁。

  //原本写法for(float timer = 0.0f; timer < 3.0f ; timer += Time.DeltaTime){yield return 0;//挂起,下一帧再来从这个位置继续执行。 } //使用WaitForSeconds的写法 yield return new WaitForSeconds(3.0f);

协程使用示例

接下来就展示下,协程使用的示例:
首先编写好协程函数

IEnumerator TestWaitForSeconds()
{//3s后执行Debug.Log; yield return new WaitForSeconds(3.0f); Debug.Log("启动协程3s后"); }

然后在某个地方使用StartCoroutine(TestWaitForSeconds())或者StartCoroutine("TestWaitForSeconds")

  //启动协程:3s后执行Debug.logStartCoroutine(TestWaitForSeconds());//启动后,继续往下执行...

Invoke的缺陷

另外一提,Unity还有个一样也是用于延时调用的函数,叫Invoke

Invoke("test",2.0f); \\延时2秒后执行函数test

但是Invock所要调用的函数必须是空类型返还值,还必须得是在当前类里面的方法。

一般来说,用协程来解决这样的问题已经绰绰有余,而且还有更安全的调用方法而不是只用string类型作为参数的方法,因此没必要使用Invoke。

协程语法

开启协程

StartCoroutine(string methodName);
  • 参数是方法名(字符串类型),此方法可以包含一个参数。
  • 形参方法可以有返回值
StartCoroutine(IEnumerator method);
  • 参数是方法(TestMethod()),此方法中可以包含多个参数。
  • IEnumrator类型的方法不能含有ref或者out类型的参数,但可以含有被传递的引用
  • 形参方法必须有返回值,且返回值类型为IEnumrator,返回值使用(yield retuen +表达式或者值,或者 yield break)语句

终止协程

StopCoroutine(string methodName);//终止指定的协程
  • 在程序中调用StopCoroutine()方法只能终止以字符串形式启动的协程
StopAllCoroutine();//终止所有协程

挂起

//程序在下一帧中从当前位置继续执行
yield return 0; //程序在下一帧中从当前位置继续执行 yield return null; //程序等待N秒后从当前位置继续执行 yield return new WaitForSeconds(N); //在所有的渲染以及GUI程序执行完成后从当前位置继续执行 yield new WaitForEndOfFrame(); //所有脚本中的FixedUpdate()函数都被执行后从当前位置继续执行 yield new WaitForFixedUpdate(); //等待一个网络请求完成后从当前位置继续执行 yield return WWW; //等待一个xxx的协程执行完成后从当前位置继续执行 yield return StartCoroutine(xxx); //如果使用yield break语句,将会导致协程的执行条件不被满足,不会从当前的位置继续执行程序,而是直接从当前位置跳出函数体,回到函数的根部 yield break;

协程的执行原理

协程函数的返回值时IEnumerator,它是一个迭代器,可以把它当成执行一个序列的某个节点的指针。
它提供了两个重要的接口,分别是Current(返回当前指向的元素)和MoveNext()(将指针向后移动一个单位,如果移动成功,则返回true)。

yield关键词用来声明序列中的下一个值或者是一个无意义的值。

如果使用yield return x(x是指一个具体的对象或者数值)的话,
那么MoveNext返回为true并且Current被赋值为x,如果使用yield break使得MoveNext()返回为false。
如果MoveNext函数返回为true意味着协程的执行条件被满足,则能够从当前的位置继续往下执行。否则不能从当前位置继续往下执行。

作者:KillerAery 出处:http://www.cnblogs.com/KillerAery/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

转载于:https://www.cnblogs.com/Roz-001/p/11205700.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/410632.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

差异表达基因热图怎么看_多变的热图1(新手专用)

热图&#xff08;heatmap&#xff09;用不同的颜色和颜色的深浅来直观的展示数据之间的差异。在测序类的文章里&#xff0c;几乎必有一幅热图用来展示差异表达基因。很多工具都可以完成热图的制作&#xff0c;今天这篇文章主要介绍利用R语言的 pheatmap包制作热图的简单小例子。…

金属材料手册_不锈钢品种手册简化版

作者&#xff1a;王鹤 / Z0012737 /一、不锈钢品种概况 1. 不锈钢的分类与用途不锈钢种类繁多&#xff0c;按组织结构可分为铁素体不锈钢、奥氏体不锈钢、马氏体不锈钢、双相不锈钢和沉淀硬化不锈钢。按化学成分可分为铬锰镍系、铬镍系不锈钢和铬系不锈钢&#xff0c;分别对应…

转:GridView 模板列中的数据绑定

<asp:TemplateField HeaderText"姓名"> <ItemTemplate> <%# Eval("FirstName") %> <%# Eval("LastName") %> </ItemTemplate><asp:Templat…

街篮混服服务器信息,街篮手游闻鸡起舞服务器火爆开启

街篮手游闻鸡起舞 金鸡独立服务器火爆开启&#xff0c;第一手游网小编已经给大家整理好了&#xff0c;想必玩家们都对这款游戏的内容非常的期待。所以现在第一时间把这篇攻略分享给玩家们&#xff0c;希望你们喜欢。也希望能对大家有所帮助&#xff0c;下面就请大家一起跟着小编…

wringPi 初始化GPIO 为上拉_GPIO接口

//阅读完大约10min//本文中的正文&#xff0c;改编自以下链接评论&#xff0c;由于非常全面明晰&#xff0c;所以我将之采纳。STM32的I/O口的8种工作模式-OpenEdv-开源电子网​www.openedv.com前言首先了解一下GPIO是什么&#xff1f;GPIO&#xff08;英语&#xff1a;General-…

mc服务器右上角信号格,手机右上角的网络信号标识是什么意思?

E指EDGE网络&#xff0c;属2.75G。移动卡的手机图标有这三种G&#xff0c;E&#xff0c;T或者(H)移动用户&#xff1a;G&#xff1a;全称&#xff1a;gprs既传输速率理论的峰值为114Kbps&#xff0c;这是早其的无线网络传输方式。E&#xff1a;全称&#xff1a;EDGE既传输速率在…

nginx下只能通过域名,禁止使用ip访问

今天来了一个需求&#xff0c;ip访问返回500&#xff0c;域名访问正常&#xff0c;只需在nginx.conf中添加 server { listen 80 default; #default 必须加的return 500; } 也可以把这些流量收集起来&#xff0c;导入到自己的网站&#xff0c;只要做以下跳转设置就可以&#…

java接口构建英雄属性_Python接口类的多继承以及抽象类的单继承

一、接口类(面向对象开发的思想和规范)的多继承需求&#xff1a;定义一个tiger类&#xff1a;会走&#xff0c;会游定义一个hawk类&#xff1a;会走&#xff0c;会飞定义一个swan类&#xff1a;会走&#xff0c;会游&#xff0c;会飞(a)如果像以下代码的方式&#xff0c;则不能…

热血江湖战无止境与服务器连接不稳定,《热血江湖》V14.0“战无止境”新版玩不停...

《热血江湖》作为一款经典武侠网游&#xff0c;将韩国游戏风格与中国武侠元素融合得恰到好处&#xff0c;打造出极具趣味性的江湖世界。流畅的战斗动作&#xff0c;华丽的技能特效&#xff0c;通过3D的演绎烘托出惟妙惟肖的视觉效果。而在《热血江湖》V14.0“战无止境”中&…

服务器操作系统的安装步骤,服务器操作系统的安装步骤

服务器操作系统的安装步骤 内容精选换一换同步lib之前请确保已经完成增加设备。请确保DDK版本号与设备侧安装的软件包版本保持一致。请确保已经打开工程的target(可以通过工程的.project查看)和所同步设备的target保持一致&#xff0c;如果不一致&#xff0c;可以参见修改工程属…

docker 镜像 退出 保存_Docker保存修改后的镜像

我们运行的容器可能在镜像的基础上做了一些修改&#xff0c;有时候我们希望保存起来&#xff0c;封装成一个新的镜像。可以使用docker提供commit功能。现在以Ubuntu为例&#xff0c;在一个裸的Ubuntu上面安装vim编辑器&#xff0c;并且把这个修改保存下来&#xff0c;封装成一个…

当输入www.baidu.com会发生什么

&#xff08;1&#xff09;首先浏览器会先判断URL是否符合URL标准&#xff0c;如果不符合就交给搜索引擎。 &#xff08;2&#xff09;如果是正确的URL&#xff0c;浏览器会先检索该主机的HOST表&#xff0c;如果存在该域名的IP&#xff0c;则直接去访问&#xff1b;如果没有&a…

vld检测不输出_专业分享丨高频电刀的质量控制检测

作 者&#xff1a;梁世波&#xff0c;邓文涛单 位&#xff1a;右江民族医学院附属医院医疗设备科 (广西百色 533000)来 源&#xff1a;《医疗装备》杂志2020年第15期〔摘 要〕高频电刀是一种被广泛应用于医疗实践中的设备&#xff0c;加强质量控制和管理对其安全、有效的…

现实世界的Windows Azure 视频:新南威尔士州教育部(DET)利用Windows Azure实现在线科学测验...

新南威尔士州教育部是南半球最大的教育机构。他们想改进八年级的科学测验方式,以再现学生在实验室所做的并且相信互动在线科学测验能够测验更广泛的 技能而不仅仅是纯科学知识。然而&#xff0c;DET估计若他们同时为65,000位学生举行在线测验在服务器的基础设施上需要投资20万美…

正则不等于一个字符串_乳饮料不等于酸奶,记住一个关键词,花最少的钱买到真正的好酸奶...

昨天在直播的时候&#xff0c;很多网友提到了酸奶。相对于牛奶平淡无奇的口味&#xff0c;家里的老人和孩子们也更喜欢酸奶的口感&#xff0c;酸酸甜甜的味道也更人喜欢。只不过在购买酸奶的时候&#xff0c;有时候虽然钱花了不少&#xff0c;但是买回家的酸奶却并非真正的酸奶…

无法创建接口的实例_什么是接口?

接口接口概述接口,是java语言中一种类型,是方法的集合,如果说 类的内部封装了成员变量,构造 方法,和成员方法,那么接口的内部主要就是封装了方法,包含抽象方法,默认方法和静态方法接口是对功能的扩展们也是声明规则的一种方式 狭义的角度来看,接口指的就是java的关键字,interfa…

PrefixSpan算法原理总结

前面我们讲到频繁项集挖掘的关联算法Apriori和FP Tree。这两个算法都是挖掘频繁项集的。而今天我们要介绍的PrefixSpan算法也是关联算法&#xff0c;但是它是挖掘频繁序列模式的&#xff0c;因此要解决的问题目标稍有不同。 一、1. 项集数据和序列数据 首先我们看看项集数据和序…

如何下载python模块_Python第三方库(模块)下载和安装(使用pip命令)

进行 Python 程序开发时&#xff0c;除了使用 Python 内置的标准模块以及我们自定义的模块之外&#xff0c;还有很多第三方模块可以使用&#xff0c;这些第三方模块可以借助 Python官方提供的查找包页面&#xff08;https://pypi.org/&#xff09;找到。 使用第三方模块之前&am…

GODADDY主机用tar命令通过SSH远程打包20M以上的文件

GODADDY的虚拟主机控制面板虽然使用起来非常方便&#xff0c;对于文件管理的很多操作也非常到位&#xff0c;但是有一个非常令人头疼的问题&#xff0c;就是GODADDY在 主机控制面板中只支持小于20M的文件打包&#xff0c;这对于大部分的站长朋友来说&#xff0c;是非常不方便的…

python链表怎么定义_python:链表定义以及实现

链表&#xff08;linked list&#xff09;是一组数据项的集合&#xff0c;其中每个数据项都是一个节点的一部分&#xff0c;每个节点还包含指向下一个节点的链接. 链表的数据结构如下图所示在链表中删除操作可以通过修改指针来实现,如下图所示:插入则是调整,插入点的前后两个指…