什么是协程

在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.log
StartCoroutine(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;

协程的执行原理

TODO

Unity C#笔记 协程的更多相关文章

  1. Unity中的协程(一)

    这篇文章很不错的问题,推荐阅读英文原版: Introduction to Coroutines Scripting with Coroutines   这篇文章转自:http://blog.csdn. ...

  2. Unity中的协程是什么?

    什么是协程? 1.协程是一个分部执行,遇到条件(yield return 语句)会挂起,直到条件满足才会被唤醒继续执行后面的代码. 2.Unity在每一帧(Frame)都会去处理对象上的协程.Unit ...

  3. Unity中使用协程进行服务端数据验证手段

    近期在做项目中的个人中心的一些事情,用户头像上传,下载,本地缓存,二级缓存,压缩,这些都要做,麻雀虽小五脏俱全啊,也是写的浑浑噩噩的, 当我们在上传用户头像的时候,向服务端发送上传头像请求之前,一般都 ...

  4. 用C# 模拟实现unity里的协程

    注:需要了解C#的迭代器,不然很难理解. 之前面试有被问到unity协程的原理,以及撇开unity用纯C#去实现协程的方法.后来了解一下,确实可以的.趁这会有空,稍微总结一下. 还是结合代码说事吧: ...

  5. unity 自实现协程总结

    unity本人自实现了一个协程调用. 只是moveNext()的简单协程调用和封装,这个没什么好说的, 网上例子一大堆. 但使用的过程中遇到了几个问题. 1. 自己写的moveNext() 协程不能等 ...

  6. 学习python笔记 协程

    下面将一个经典的消费者和生产者的案例进行分析: import time def consumer(): r = '' while True: n = yield r if not n: return ...

  7. Unity XLua之协程

    如何使用xlua实现协程,示例代码如下: 转载请注明出处:https://www.cnblogs.com/jietian331/p/10735773.html local unpack = unpac ...

  8. xlua 实现协程替换Unity中的协程

    C#中的协程: IEnumerator ShowSpiritInfo() { UIMessageMgr.ShowMsgWait(true); DestroyUIModelInfo(); bool is ...

  9. python学习笔记 协程

    在学习异步IO模型前,先来了解协程 协程又叫做微线程,Coroutine 子程序或者成为函数,在所有语言中都是层级调用,比如a调用b,b调用c.c执行完毕返回,b执行完毕返回,最后a执行完毕返回 所以 ...

随机推荐

  1. Zookeeper + Dubbo + SpringMVC + dubbo-admin

    第一步:在CentOS/Windows上安装Zookeeper[前提] A:CentOS     Zookeeper作为Dubbo服务的注册中心,Dubbo原先基于数据库的注册中心,没采用Zookee ...

  2. UML语言中五大视图和九种图形纵览

    UML语言纵览 视图 UML语言中的视图大致分为如下5种: 1.用例视图.用例视图强调从系统的外部参与者(主要是用户)的角度看到的或需要的系统功能. 2.逻辑视图.逻辑视图从系统的静态结构和动态行为角 ...

  3. 【转】java中PriorityQueue优先级队列使用方法

    优先级队列是不同于先进先出队列的另一种队列.每次从队列中取出的是具有最高优先权的元素. PriorityQueue是从JDK1.5开始提供的新的数据结构接口. 如果不提供Comparator的话,优先 ...

  4. HTML DOM innerHTML 属性及实现图片连续播放

    定义和用法 innerHTML 属性设置或返回表格行的开始和结束标签之间的 HTML. 语法 tablerowObject.innerHTML=HTML 实例 下面的例子返回了表格行的 inner H ...

  5. 修改ZendStudio新建php文件时的模板

    zendstudio默认的模板不适用,可以自己到Window -- preferences -- php -- code style -- code templates -- code -- samp ...

  6. JavaScript 之函数

    刚开 始学习 JS 时,挺不习惯它函数的用法,就比如一个 function 里面会嵌套一个 function,对于函数里创建变量的作用域也感到很迷惑,这个的语法和 JAVA 相差太多,为此,阅读了&l ...

  7. Spring Data JPA 初体验

    一,JPA相关的概念 JPA概述 全称是:JavaPersistence API.是SUN公司推出的一套基于ORM的规范. Hibernate框架中提供了JPA的实现. JPA通过JDK 5.0注解或 ...

  8. VC++中字符串编码处理的一些相关问题

    前言 什么是tchar? 百度百科对其的定义如下": 因为C++支持两种字符串,即常规的ANSI编码(使用""包裹)和Unicode编码(使用L""包 ...

  9. S3C6410板子移植 Android2.2

    一:Android简介 1.什么是Android: Android是一种基于linux的自由及开放源代码的操作系统,主要适用于移动设备,如智能手机和平板电脑,是由google公司和开放手机联盟领导和开 ...

  10. Android 实现形态各异的双向侧滑菜单 自定义控件来袭

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39670935,本文出自:[张鸿洋的博客] 1.概述 关于自定义控件侧滑已经写了两 ...