協程(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;
    }
}
相關文章
相關標籤/搜索