1. 程式人生 > >unity 協程原理與執行緒的區別

unity 協程原理與執行緒的區別

說到協程,我們首先回顧以下執行緒與程序這兩個概念。在作業系統(os)級別,有程序(process)和執行緒(thread)兩個我們看不到但又實際存在的“東西”,這兩個東西都是用來模擬“並行”的,寫作業系統的程式設計師通過用一定的策略給不同的程序和執行緒分配CPU計算資源,來讓使用者“以為”幾個不同的事情在“同時”進行“。在單CPU上,是os程式碼強制把一個程序或者執行緒掛起,換成另外一個來計算,所以,實際上是序列的,只是“概念上的並行”。在現在的多核的cpu上,執行緒可能是“真正並行的”。

1 執行緒、程序、協程的區別

程序擁有自己獨立的堆和棧,既不共享堆,亦不共享棧,程序由作業系統排程。

執行緒擁有自己獨立的棧和共享的堆,共享堆,不共享棧,執行緒亦由作業系統排程(標準執行緒是的)。

協程和執行緒一樣共享堆,不共享棧,協程由程式設計師在協程的程式碼裡顯示排程。

一個應用程式一般對應一個程序,一個程序一般有一個主執行緒,還有若干個輔助執行緒,執行緒之間是平行執行的,線上程裡面可以開啟協程,讓程式在特定的時間內執行。

協程和執行緒的區別是:協程避免了無意義的排程,由此可以提高效能,但也因此,程式設計師必須自己承擔排程的責任,同時,協程也失去了標準執行緒使用多CPU的能力。

打個比方吧,假設有一個作業系統,是單核的,系統上沒有其他的程式需要執行,有兩個執行緒 A 和 B ,A 和 B 在單獨執行時都需要 10 秒來完成自己的任務,而且任務都是運算操作,A B 之間也沒有競爭和共享資料的問題。現在 A B 兩個執行緒並行,作業系統會不停的在 A B 兩個執行緒之間切換,達到一種偽並行的效果,假設切換的頻率是每秒一次,切換的成本是 0.1 秒(主要是棧切換),總共需要 20 + 19 * 0.1 = 21.9 秒。如果使用協程的方式,可以先執行協程 A ,A 結束的時候讓位給協程 B ,只發生一次切換,總時間是 20 + 1 * 0.1 = 20.1 秒。如果系統是雙核的,而且執行緒是標準執行緒,那麼 A B 兩個執行緒就可以真並行,總時間只需要 10 秒,而協程的方案仍然需要 20.1 秒。
這裡寫圖片描述

Unity協程執行原理

unity中協程執行過程中,通過yield return XXX,將程式掛起,去執行接下來的內容,注意協程不是執行緒,在為遇到yield return XXX語句之前,協程額方法和一般的方法是相同的,也就是程式在執行到yield return XXX語句之後,接著才會執行的是 StartCoroutine()方法之後的程式,走的還是單執行緒模式,僅僅是將yield return XXX語句之後的內容暫時掛起,等到特定的時間才執行。
那麼掛起的程式什麼時候才執行,這就要看monoBehavior的生命週期了。
這裡寫圖片描述
也就是協同程式主要是update()方法之後,lateUpdate()方法之前呼叫的,接下來我們通過一個小例子去理解一下。

using UnityEngine;
using System.Collections;
using System.Threading;
public class test : MonoBehaviour
{

    void Start()
    {
        StartCoroutine(tt());//開啟協程
        for (int i = 0; i < 200; i++)   //迴圈A
        {
            Debug.Log("*************************" + i);
            Thread.Sleep(10);
        }
    }


    IEnumerator tt()
    {
        for (int i = 0; i < 100; i++) //迴圈B
        {
            Debug.Log("-------------------" + i);
        }

        yield return new WaitForSeconds(1); //協程1

        for (int i = 0; i < 100; i++) //迴圈C
        {
            Debug.Log(">>>>>>>>>>>>>>>>>>>>" + i);
            yield return null; //協程1
        }
    }

    // 更新資料
    void Update()
    {
        Debug.Log("Update");
    }

    //晚於更新
    void LateUpdate()
    {
        Debug.Log("------LateUpdate");
    }


}

程式的執行結果為:
這裡寫圖片描述
先執行迴圈B,然後執行迴圈A,然後執行update()和lateUpdate()的方法,等待1S之後,在updat()和lateupda()之間執行迴圈C的輸出。