自: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; } }