協程(Coroutine)並不是真正的多線程

來源:http://www.cnblogs.com/wonderKK/p/4062591.html



說到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能分段執行的機制。

 

 

協程並不是真正的多線程,下面這段代碼在協程中加入死循環,運行就卡住了。

 

複製代碼
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;
    }
}
複製代碼

 

協程(一)原理 - prepat - 博客園
http://www.cnblogs.com/takeaction/archive/2015/03/25/4365422.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章