Unity3D中的線程與協程


線程 

  Unity3D是以生命周期主線程循環進行游戲開發。

  Unity3D中的子線程無法運行Unity SDK(開發者工具包,軟件包、軟件框架)跟API(應用程序編程接口,函數庫)。

  限制原因:大多數游戲引擎都是主循環結構,游戲中邏輯更新和畫面更新的時間點要求有確定性,必須按照幀序列嚴格保持同步,否則就會出現游戲中的對象不同步的現象。雖然多線程也能保證這個效果,但是引用多線程,會加大同步處理的難度與游戲的不穩定性。

  但是多線程也是有好處的,如果不是畫面更新,也不是常規的邏輯更新(指包括AI、物理碰撞、角色控制這些),而是一些其他后台任務,比如大量耗時的數據計算、網絡請求、復雜密集的I/O操作,則可以將這個獨立出來做成一個工作線程,這需要寫Unity游戲的Native擴展。

協程

  對於Unity3D,它是生命周期主線程循環的設計,它更傾向於使用Time slicing(時間分片)的Coroutine(協程)去完成異步任務,融合到生命周期中。

  線程是操作系統級別的概念,現代操作系統都支持並實現線程,線程的調度對應用開發者是透明的,開發者無法預期某線程在何時被調度執行。基於此,一般那種隨機出現的BUG,多與線程調度相關。

  而協程Coroutine是編譯器級別的,本質是一個線程時間片去執行代碼段。它通過相關的代碼使得代碼段能夠實現分段式的執行,顯式調用yield函數后才被掛起,重新開始的地方是yield掛起的位置,每一次執行協程會跑到下一個yield語句。協程能保留上一次調用時的狀態(即所有局部狀態的一個特定組合),每次過程重入時,就相當於進入上一次調用的狀態,換種說法:進入上一次離開時所處邏輯流的位置。

  在Unity3D中,協程是可自行停止運行 (yield),直到給定的 YieldInstruction 結束再繼續運行的函數。協程 (Coroutines) 的不同用途: ·

       (1) yield return null - 這一幀到此暫停,下一幀再從暫停處繼續,常用於循環中。

       (2) yield return new WaitForEndOfFrame - 等到這一幀的cameras和GUI渲染結束后再從此處繼續,即等到這幀的末尾再往下運行。這行之后的代碼還是在當前幀運行,是在下一幀開始前執行,跟return null很相似。

       (3) yield return new WaitForFixedUpdate - 在下一次執行FixedUpdate的時候繼續執行這段代碼,即等一次物理引擎的更新。

       (4) yield return new WaitForSeconds(3.0f) - 等待3秒,然后繼續從此處開始,常用於做定時器。

       (5) yield return WWW - 等待直至異步下載完成。

       (6) yield return StartCoroutine(methodName) - 等待另一個協程執行完。這是把協程串聯起來的關鍵,常用於讓多個協程按順序逐個運行。

  (7)yield break - 直接跳出協程,對某些判定失敗必須跳出的時候,比如加載AssetBundle的時候,WWW失敗了,后邊加載bundle沒有必要了,這時候可以yield break跳出。

  值得注意的是 WaitForSeconds()受Time.timeScale影響,當Time.timeScale = 0f 時,yield return new WaitForSecond(x) 將不會滿足。

 

  以下為Unity3D的生命周期循環圖

c#代碼示例

Unity3D使用協程常需要用到輔助類Stopwatch,提供一組可用於准確地測量運行時間的方法和屬性。

private Stopwatch frameStopwatch;//用來記錄上一幀結束到現在所用的時間
private float targetFrameDuration;//自定義的每幀持續時間,防止協程過度消耗線程時間片
private void Awake()
{
    frameStopwatch = new Stopwatch();
}

void Update()
{
    //計算每一幀所用的時間Start()之后Elapsed會一直增加,Stop()之后Elapsed的值就不變
    frameStopwatch.Stop();
    frameStopwatch.Reset();
    frameStopwatch.Start();

  if(ChunkUpdateList.Count > 0)
  {
    StartCoroutine(ProcessChunkQueueLoop());//啟動協程處理ChunkUpdateList
  } }

private IEnumerator ProcessChunkQueueLoop()
{
    while (ChunkUpdateList.Count > 0)
    {
        ProcessChunkUpdateList();//每次處理ChunkUpdateList中的一個數據
        if (frameStopwatch.Elapsed.TotalSeconds >= targetFrameDuration)//這一幀已經運行的時間frameStopwatch.Elapsed.TotalSeconds已經超過自定義每幀的時間targetFrameDuration,則掛起直到這一幀結束再運行
        {
            yield return new WaitForEndOfFrame();
        }
    }
}

 

引用:

  (1)游戲引擎Unity中的單線程與多線程

其他:

  (1)游戲主循環

  (2)3D引擎多線程:渲染與邏輯分離

  


免責聲明!

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



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