【Unity3D/C#】Unity3D中的Coroutine詳解


Unity中的coroutine是通過yield expression;來實現的。官方腳本中到處會看到這樣的代碼。

疑問:

yield是什么?

Coroutine是什么?

unity的coroutine程序執行流程怎么那么奇怪?

unity中的coroutine原理是什么,怎么實現的?

使用unity的coroutine需要注意什么問題?

 

一、yield的在幾種語言中的程序執行特性

     Lua中的yield是使得協同函數運行->掛起並且傳遞參數給resume。resume使得協同函數掛起->運行並且傳遞參數給協同函數。

     C#中yield return/break是用於函數查詢集合生成器里面的值(類似迭代)返回,並記錄當前現場,下次查詢時從上一次記錄的yield現場處,繼續往下執行,直到繼續往下執行沒有了,那么退出這段yield的邏輯。yield break會終止掉yield迭代邏輯並跳出。
YieldImplementation:
   1).Caller callsfunction
   2).Caller requestsitem 按需請求一個元素
   3).Next itemreturned 返回請求的元素
   4).Goto step #2

    Python中的yield expression, 有yield的函數就變成了一個生成器,調用該函數返回的是迭代器對象,用迭代器對象調用next方法(或者循環中會自動調用next方法),才開始執行函數,執行到yield expression處則中斷,返回迭代器當前的值,並保留現場,下次調用next則從現場處開始執行,迭代完了就停止了。可以看出Python中的yield和C#中的yield是類似的,用於創建生成器,執行時中斷返回迭代器值,並記錄現場,下次從現場處繼續執行。

   Unity中的yield就是和C#,python中的類似,因為unity是基於.net框架的,且unity腳本開始是用Boo(Python的一個變種)寫的。只是unity中多了coroutine特性類型,和StartCoroutine的coroutine管理類。StartCoroutine不是啟動了一個新的線程,而是開啟一個協同程序,默認unity所有代碼都在一個線程中(http://answers.unity3d.com/questions/280597/new-thread-vs-startcoroutine.html)。

coroutine語言層面的原理:

 

在兩年前,協程似乎是一個很高級的東西,隨后大多數語言或多或少都支持協程。我比較熟悉的有Python的gevent,Lua的coroutine,Go的goroutine。尤其是Lua和Go,語言本身就支持協程。協程也被叫做輕量級線程。通俗點講就是定義一大堆任務,然后通過一個線程輪着對每個任務都執行一下,協作運行。它的厲害之處在於每運行到一個任務的時候,它都可以從這個任務上一次中斷的地方開始運行。在我們一般的印象中,只有操作系統對線程進行調度的時候才會干這樣的事情,進行各種進棧,保存狀態。而協程,總共也只是運行在一個線程中,要是使用線程本身的系統棧,早就暴了。因此在這里,實現的時候是用內存來模擬棧的操作。具體實現,我想復雜度一定會不小。

我們知道,線程比進程輕量級,因此產生一個線程消耗的資源比進程少,上下文切換也比進程節約。而協程比線程更加輕量級,上下文切換更是迅速。於是在服務器編程方面給人無限想象。盡管目前還沒有出現一款主流的采用協程的web服務器。但是Go語言開發的web服務的性能已經嶄露頭角了。

 

 

二、Unity的Coroutine執行現象

第一種方法:  

voidStart()

    {

       print("Starting " +Time.time);

       StartCoroutine(WaitAndPrint(2));

       print("Done " +Time.time);

    }

   IEnumerator WaitAndPrint(float waitTime)

    {

       yield return new WaitForSeconds(waitTime);

       print("WaitAndPrint " + Time.time);

    }

該段代碼的執行順序是12435 

執行到4協程注冊了事件,控制權交給外部線程;外部線程執行到3;事件發生,程序分段執行機制goto到協程處記錄了堆棧信息執行5語句。

 // Use this for initialization
    void Start()
    {
        StartCoroutine(IEnumeratorStart());
    }

    // Update is called once per frame
    void Update()
    {

    }

    IEnumerator IEnumeratorStart()
    {
       print("Starting " + Time.time); 
       yield return StartCoroutine(WaitAndPrint(2.0F));
       print("Done " + Time.time);
    }

    IEnumerator WaitAndPrint(float waitTime)
    {
       yield return new WaitForSeconds(waitTime); 
       print("WaitAndPrint " + Time.time); 
    }

該段代碼的執行順序是12453

Why?這么奇怪的執行方式。

程序執行到4,執行yield return表達式注冊事件交出控制權給外部,因為外部還要交出控制權也需要執行yield return后面的表達式語句因此會重入WaitAndPrint函數接着協程當前狀態下一步執行所以執行到5,yield return 后面表達式語句執行完畢控制權完全交出,之后才執行3,根本原因是yield return 不能直接嵌套后面需要跟一個表達式(事件)。

五、Unity中使用Coroutine需要注意的問題:

1.使用的地方和不能使用的地方:

必須在MonoBehaviour或繼承於MonoBehaviour的類中調用 yield coroutine。yield不可以在Update或者FixedUpdate里使用。

 

2.開啟協程:

StartCoroutine(string methodName)和StartCoroutine(IEnumeratorroutine)都可以開啟一個協程,

區別:

使用字符串作為參數時,開啟協程時最多只能傳遞一個參數,並且性能消耗會更大一點; 而使用IEnumerator 作為參數則沒有這個限制。

 

3.刪除協程:

1).在Unity3D中,使用StopCoroutine(stringmethodName)來終止該MonoBehaviour指定方法名的一個協同程序,使用StopAllCoroutines()來終止所有該MonoBehaviour可以終止的協同程序。

包括StartCoroutine(IEnumerator routine)的。

2).還有一種方法可以終止協同程序,即將協同程序所在gameobject的active屬性設置為false,當再次設置active為ture時,協同程序並不會再開啟;

如是將協同程序所在腳本的enabled設置為false則不會生效。

 

4.js和C#中使用區別:

在C#中要使用 yield return而不是yield。

C#中yield(中斷)語句必須要在IEnumerator類型里,C#方法的返回類型為IEnumerator,返回值如(eg:yield return new WaitForSeconds(2); 或者 yield returnnull);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM