協(xié)程與多線程

一、協(xié)程

協(xié)程其實(shí)就是一個(gè)IEnumerator(迭代器),
IEnumerator 接口有兩個(gè)方法 Current 和 MoveNext()

Unity在每幀做的工作就是:調(diào)用 協(xié)程(迭代器)MoveNext() 方法,如果返回 true ,就從當(dāng)前位置繼續(xù)往下執(zhí)行。

協(xié)程是什么?
協(xié)同程序,即在主程序運(yùn)行時(shí)同時(shí)開啟另一段邏輯處理,來協(xié)同當(dāng)前程序的執(zhí)行。換句話說,開啟協(xié)同程序就是開啟一個(gè)線程。

協(xié)程是一個(gè)分部執(zhí)行,遇到條件(yield return 語句)會(huì)掛起,直到條件滿足才會(huì)被喚醒繼續(xù)執(zhí)行后面的代碼。通俗點(diǎn)說:程序內(nèi)部可中斷,然后轉(zhuǎn)而執(zhí)行別的子程序,在適當(dāng)?shù)臅r(shí)候再返回來接著執(zhí)行。

協(xié)程的運(yùn)行
協(xié)程不是線程,也不是異步執(zhí)行的。協(xié)程和 MonoBehaviour 的 Update函數(shù)一樣也是在MainThread中執(zhí)行的。
協(xié)同程序可以和主程序并行運(yùn)行,和多線程有點(diǎn)類似。

協(xié)程的作用
1、延時(shí)(等待)一段時(shí)間執(zhí)行代碼;
2、等某個(gè)操作完成之后再執(zhí)行后面的代碼。
總結(jié)起來就是一句話:控制代碼在特定的時(shí)機(jī)執(zhí)行。

Paste_Image.png

WaitForSeconds()受Time.timeScale影響,當(dāng)Time.timeScale = 0f 時(shí),yield return new WaitForSecond(x) 將不會(huì)滿足。

練習(xí)測試

    void Start () {
        print("開始下載" + "---" + Time.time);
        StartCoroutine(waitOne());
        print("正在下載----50%" + "---" + Time.time);

    }
    IEnumerator waitOne()
    {
        Debug.Log("加入下載列表" + "---" + Time.time);
        yield return StartCoroutine(waitTwo());
        print("下載完成" + "---" + Time.time);
    }
    IEnumerator waitTwo()
    {
        print("正在下載----1%" + "---" + Time.time);
        yield return new WaitForSeconds(3);
        print("正在下載----70%" + "---" + Time.time);
    }

打印結(jié)果(執(zhí)行順序):


Paste_Image.png

禁用該腳本協(xié)程是否還會(huì)執(zhí)行?


Paste_Image.png

二、WaitUntil和WaitWhile的秘密

開始學(xué)習(xí)WaitUntil
θ 根據(jù)定義,它掛起語句,直到指定的條件返回true。
θ 換句話說,當(dāng)我們指定的條件是false時(shí),它不會(huì)繼續(xù)執(zhí)行語句。Unity將會(huì)等待條件返回true。

測試

 public int counter;
    void Start ()
    {
        counter = 0;
        StartCoroutine(FuelNotification());
    }
    IEnumerator FuelNotification()
    {
        Debug.Log("開始通關(guān)" + "---" + Time.time);
        yield return new WaitUntil(IsTankEmpty);
        Debug.Log("通關(guān)了!" + "---" + Time.time);
    }
    void Update()
    {
        if (counter<21)
        {
            Debug.Log("通關(guān)數(shù)量:"+counter+"---"+Time.time);
            counter++;
        }
    }
    public bool IsTankEmpty()
    {
        if (counter<21)
        {
            return false;
        }
        else
        {
            return true;
        }
    }

打印結(jié)果:


Paste_Image.png

下面我們?cè)賮砜纯丛趺词褂肔ambda表達(dá)式。Lambda表達(dá)式是一個(gè)非常便利的工具,試一試吧。

 public int counter;
    void Start()
    {
        counter = 20;
        StartCoroutine(FuelNotification());
    }
    void Update()
    {
        if (counter > 0)
        {
            Debug.Log("Fuel Level:" + counter + "---" + Time.time);
            counter--;
        }
    }
    IEnumerator FuelNotification()
    {
        Debug.Log("Waiting for tank to get empty" + "---" + Time.time);
        yield return new WaitUntil(() => counter <= 0);
        Debug.Log("Tank Empty!" + "---" + Time.time);
    }

打印結(jié)果 :

Paste_Image.png

現(xiàn)在來學(xué)習(xí)WaitWhile:WaitWhile是WaitUntil的對(duì)立面。它抑制表達(dá)式,當(dāng)條件為真。不像WaitUntil,它將會(huì)等待條件變?yōu)閒alse,才能指向后面的代碼塊。和前面同樣的例子,我們使用WaitWhile:我們只需要改變Lambda表達(dá)式,讓其等于False,其打印結(jié)果是一樣的。

    public int counter;
    void Start()
    {
        counter = 20;
        StartCoroutine(FuelNotification());
    }
    void Update()
    {
        if (counter > 0)
        {
            Debug.Log("Fuel Level:" + counter + "---" + Time.time);
            counter--;
        }
    }
    IEnumerator FuelNotification()
    {
        Debug.Log("Waiting for tank to get empty" + "---" + Time.time);
        yield return new WaitUntil(() => counter > 0);
        Debug.Log("Tank Empty!" + "---" + Time.time);
    }
image.png

三、線程與協(xié)程的區(qū)別

線程

線程之間共享變量,解決了通訊麻煩的問題,但是對(duì)于變量的訪問需要鎖,線程的調(diào)度主要也是有操作系統(tǒng)完成,一個(gè)進(jìn)程可以擁有多個(gè)線程,但是其中每個(gè)線程會(huì)共享父進(jìn)程向操作系統(tǒng)申請(qǐng)資源,這個(gè)包括虛擬內(nèi)存、文件等,由于是共享資源,所以創(chuàng)建線程所需要的系統(tǒng)資源占用比進(jìn)程小很多,相應(yīng)的可創(chuàng)建的線程數(shù)量也變得相對(duì)多很多。線程時(shí)間的通訊除了可以使用進(jìn)程之間通訊的方式以外還可以通過共享內(nèi)存的方式進(jìn)行通信,所以這個(gè)速度比通過內(nèi)核要快很多。另外在調(diào)度方面也是由于內(nèi)存是共享的,所以上下文切換的時(shí)候需要保存的東西就像對(duì)少一些,這樣一來上下文的切換也變得高效。

協(xié)程

協(xié)程的調(diào)度完全由用戶控制,一個(gè)線程可以有多個(gè)協(xié)程,用戶創(chuàng)建了幾個(gè)線程,然后每個(gè)線程都是循環(huán)按照指定的任務(wù)清單順序完成不同的任務(wù),當(dāng)任務(wù)被堵塞的時(shí)候執(zhí)行下一個(gè)任務(wù),當(dāng)恢復(fù)的時(shí)候再回來執(zhí)行這個(gè)任務(wù),任務(wù)之間的切換只需要保存每個(gè)任務(wù)的上下文內(nèi)容,就像直接操作棧一樣的,這樣就完全沒有內(nèi)核切換的開銷,可以不加鎖的訪問全局變量,所以上下文的切換非常快;另外協(xié)程還需要保證是非堵塞的且沒有相互依賴,協(xié)程基本上不能同步通訊,多采用一步的消息通訊,效率比較高。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 原文鏈接:https://github.com/EasyKotlin 在常用的并發(fā)模型中,多進(jìn)程、多線程、分布式是...
    JackChen1024閱讀 10,885評(píng)論 3 23
  • Coroutine in Python 引言: 本文出自David Beazley 的關(guān)于協(xié)程的PPT,現(xiàn)在筆者將...
    LumiaXu閱讀 1,712評(píng)論 4 8
  • 目錄 一、開啟線程的兩種方式 在python中開啟線程要導(dǎo)入threading,它與開啟進(jìn)程所需要導(dǎo)入的模塊mul...
    CaiGuangyin閱讀 2,469評(píng)論 1 16
  • Object C中創(chuàng)建線程的方法是什么?如果在主線程中執(zhí)行代碼,方法是什么?如果想延時(shí)執(zhí)行代碼、方法又是什么? 1...
    AlanGe閱讀 1,918評(píng)論 0 17
  • 八月初,和朋友一起踏上了飛往南方的飛機(jī)。這可是第一次坐飛機(jī),未免有些小緊張,不過也沒什么。 身為北方少年的我,還是...
    靑檸閱讀 457評(píng)論 0 3

友情鏈接更多精彩內(nèi)容