协程(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系统的位置,下面一张图能够说明:

注:图片来自Coroutines++

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能分段执行的机制。ui

 

 

协程并非真正的多线程,下面这段代码在协程中加入死循环,运行就卡住了。url

 

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 = 0;

    void Start()
    {
        StartCoroutine(downLoad());
        StartCoroutine(Add());
        add = 0;
    }
    
    // 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(5);
        Debug.Log("1");
        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;
    }
}
相关文章
相关标签/搜索