协程(Coroutine)并不是真正的多线程(转)
自:http://www.zhihu.com/question/23895384
说到Coroutine,我们必须提到两个更远的东西。在操作系统(os)级别,有进程(process)和线程(thread)两个(仅从我们常见的讲)实际的“东西”(不说概念是因为这两个家伙的确不仅仅是概念,而是实际存在的,os的代码管理的资源)。这两个东西都是用来模拟“并行”的,写操作系统的程序员通过用一定的策略给不同的进程和线程分配CPU计算资源,来让用户“以为”几个不同的事情在“同时”进行“。在单CPU上,是os代码强制把一个进程或者线程挂起,换成另外一个来计算,所以,实际上是串行的,只是“概念上的并行”。在现在的多核的cpu上,线程可能是“真正并行的”。
Coroutine,翻译成”协程“,初始碰到的人马上就会跟上面两个概念联系起来。直接先说区别,Coroutine是编译器级的,Process和Thread是操作系统级的。Coroutine的实现,通常是对某个语言做相应的提议,然后通过后成编译器标准,然后编译器厂商来实现该机制。Process和Thread看起来也在语言层次,但是内生原理却是操作系统先有这个东西,然后通过一定的API暴露给用户使用,两者在这里有不同。Process和Thread是os通过调度算法,保存当前的上下文,然后从上次暂停的地方再次开始计算,重新开始的地方不可预期,每次CPU计算的指令数量和代码跑过的CPU时间是相关的,跑到os分配的cpu时间到达后就会被os强制挂起。Coroutine是编译器的魔术,通过插入相关的代码使得代码段能够实现分段式的执行,重新开始的地方是yield关键字指定的,一次一定会跑到一个yield对应的地方。
对于Coroutine,下面是一个实现的function,里面的片段被yield关键字分成2段:
IEnumerator YieldSomeStuff()
{
yield "hello";
Console.WriteLine("foo!");
yield "world";
}
推进的代码(模拟,非实际):
IEnumerator e = YieldSomeStuff();
while(e.MoveNext())
{
Console.WriteLine(e.Current);
}
以此来推进整个代码片段的分段执行。更详细的分析如 @邓凯的文章里提到。这里只要说明的是,对于Coroutine,是编译器帮助做了很多的事情,来让代码不是一次性的跑到底,而不是操作系统强制的挂起。代码每次跑多少,是可预期的。但是,Process和Thread,在这个层面上完全不同,这两个东西是操作系统管理的。在unity中,StartCoroutine这个方法是个推进器。StartCoroutine会发起类似上面的while循环。因为是while循环,因此,Coroutine本身其实不是“异步的”。
Coroutine在整个Unity系统的位置,下面一张图可以说明:
Unity官方文档里也写到"Normal Coroutine在Update之后"的字眼,如下内容第一行:
Normal coroutine updates are run after the Update function returns. A coroutine is a function that can suspend its execution (yield) until the given YieldInstruction finishes. Different uses of Coroutines:
yield; The coroutine will continue after all Update functions have been called on the next frame.
yield WaitForSeconds(2); Continue after a specified time delay, after all Update functions have been called for the frame
yield WaitForFixedUpdate(); Continue after all FixedUpdate has been called on all scripts
yield WWW Continue after a WWW download has completed.
yield StartCoroutine(MyFunc); Chains the coroutine, and will wait for the MyFunc coroutine to complete first.
由上面的图和文字可以大致猜测,.net虚拟机在每一帧循环中,会依次进入每个编译器预定义好的入口。对于Coroutine,编译器需要产生一些代码,在每次的大循环中,Unity的Update()返回后,保证是yield后的代码被正确调用,这样就形成了我们看到的一个function能分段执行的机制。
协程并不是真正的多线程,下面这段代码在协程中加入死循环,运行就卡住了。
using UnityEngine;
using System.Collections; public class WWWtest : MonoBehaviour { public string url = "http://images.earthcam.com/ec_metros/ourcams/fridays.jpg";
private WWW www;
public UILabel label;
private uint add = ; void Start()
{
StartCoroutine(downLoad());
StartCoroutine(Add());
add = ;
} // Update is called once per frame
void Update () {
label.text = add.ToString();
} IEnumerator downLoad()
{
yield return null;
Debug.Log("start 1");
yield return new WaitForSeconds();
Debug.Log("");
www = new WWW(url);
Debug.Log("www start");
yield return www;
GetComponent<GUITexture>().texture = www.texture;
Debug.Log("www done");
} IEnumerator Add()
{
while (true) ;
yield return null;
}
}
最新文章
- 【第1期】腾讯云的1001种玩法征集,Ipad mini和Kindle 等你拿!(文章评审中)
- Python 之 lamda 函数
- U盘安装中标麒麟服务器操作系统 一 (NeoKylin 6.5)
- visibility和display的异同
- Jmeter调试工具---HTTP Mirror Server
- 深刻理解Java中final的作用(一):从final的作用剖析String被设计成不可变类的深层原因
- swift app中展示折线图, 饼状图, 柱状图等数据图表
- MailSystem.NET Gmail IMAP讀取信件
- 基于HTTP协议下载文件的实现
- 转:Warning -26490: File name in a multipart submit is missing or empty.解决方法
- [系统启动]Printk与sched_clock_init的一点分析
- Kali Linux下安装Nessus扫描器
- vue-cli3.0 flexible&;px2rem 解决第三方ui组件库样式问题
- Jmeter(1)介绍
- CS231n官方笔记授权翻译总集篇发布
- P1450 [HAOI2008]硬币购物
- Kubernetes 安装
- document.all 在各浏览器中的支持不同
- CentOS75 安装 telnet 进行使用.
- 流程控制与数组——Java疯狂讲义
热门文章
- NSURLSession使用模板和AFNetworking使用模板(REST风格)
- JSP与JavaBeans
- C# 泛型的入门理解(来自网络)
- CentOS7.3系统启动故障修复
- [HEOI2014][bzoj3611] 大工程 [虚树+dp]
- 采花 flower
- Codeforces Round #321 (Div. 2) B 二分+预处理
- CSS Modules使用方法
- cmp 指令
- Berkeley DB Java Edition 简介