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
Unity C#笔记 协程详解(转)相关推荐
- python协程详解
目录 python协程详解 一.什么是协程 二.了解协程的过程 1.yield工作原理 2.预激协程的装饰器 3.终止协程和异常处理 4.让协程返回值 5.yield from的使用 6.yield ...
- python协程详解_python协程详解
原博文 2019-10-25 10:07 − # python协程详解 ![python协程详解](https://pic2.zhimg.com/50/v2-9f3e2152b616e89fbad86 ...
- Unity的协程详解
一.协程的定义 协程,即为协同程序. Unity中的协程由协程函数和协程调度器两部分构成.协程函数使用的是C#的迭代器, 协程调度器则利用了MonoBehaviour中的生命周期函数来实现. 协程函数 ...
- python协程详解_对Python协程之异步同步的区别详解
一下代码通过协程.多线程.多进程的方式,运行代码展示异步与同步的区别. import gevent import threading import multiprocessing # 这里展示同步和异 ...
- Python进程、线程、协程详解
进程与线程的历史 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源的管理和分配.任务的调度. ...
- coroutine协程详解
前两天阿里巴巴开源了coobjc,没几天就已经2千多star了,我也看了看源码,主要关注的是协程的实现,周末折腾了两整天参照Go的前身libtask和风神的coroutine实现了一部分,也看了一些文 ...
- 【转载】Python线程、进程和协程详解
从操作系统角度 操作系统处理任务,调度单位是进程和线程. 进程:表示一个程序的执行活动(打开程序.读写程序数据.关闭程序) 线程:执行某个程序时,该进程调度的最小执行单位(执行功能1,执行功能2) 一 ...
- python多核cpu_Python中的多核CPU共享数据之协程详解
一 : 科普一分钟 尽管进程间是独立存在的,不能相互访问彼此的数据,但是在python中却存在进程间的通信方法,来帮助我们可以利用多核CPU也能共享数据. 对于多线程其实也是存在一些缺点的,不是任何场 ...
- python协程详解_彻底搞懂python协程-第一篇(关键词1-4)
任何复杂的概念或系统都不是凭空出现的,我们完全可以找到它的演化历程,寻根究底终会发现,其都是在一系列并不那么复杂的简单组件上发展演化而来! by 落花僧 本文通过一系列关键概念,逐步递进理解协程. 0 ...
最新文章
- 计算机挑战音乐,抖音平板加速挑战背景歌曲是什么歌?
- newlisp 接受jenkins带空格的参数
- ACM PKU 2299 Ultra-QuickSort
- .Net Core with 微服务 - Seq 日志聚合
- php apache win7,win7安装apache+php
- 安全专家教你如何利用Uber系统漏洞无限制的免费乘坐?
- eclipse怎么升级到java ee,如何为Java EE开发人员升级Eclipse?
- 《大数据》2015年第3期“网络大数据专题”——网络大数据的文本内容分析
- 老男孩python14期全套-老男孩第十四期Python学习班之Day01
- 【C++笔记】对象模型和this指针
- MPLS virtual private network Internet接入
- 大数据平台应用 17 个关键技术处理
- 微软面试题 经典测试 (博弈论 经典案例)
- 基于spring boot 轻量级的完全开源商城 mall4j
- python - TXT章节文转为epub和mobi格式
- vue 实现二维码 vue-qart 和 qrcodejs2
- 牛津计算机科学要求,牛津布鲁克斯大学计算机科学本科申请条件.pdf
- Linux Centos 7移动电信双线 策略路由
- Mikrotik RouterOS路由器和华为交换机链路聚合+VRRP+单线多拨
- jfinal jboot 拦截器过滤文件上传请求 和 跨域解决方法
热门文章
- python文件处理seek()方法的参数是_Python 文件(File) seek() 方法
- 每日一题——leetcode237 删除链表中的结点
- 【联盛德W806上手笔记】一、开发环境和烧录程序
- 更改文件和目录(及子目录)的拥有者
- gcc工具链查看默认编译选项
- 安卓USB开发教程 三 USB Accessory
- Taro+react开发(87):图片引入
- 前端学习(2965):路由的参数传递
- [vue] vue变量名如果以_、$开头的属性会发生什么问题?怎么访问到它们的值?
- 前端学习(2355):uni里面的样式学习