關於Unity中協程、多線程、線程鎖、www網絡類的使用


協程

我們要下載一張圖片,加載一個資源,這個時候一定不是一下子就加載好的,或者說我們不一定要等它下載好了才進行其他操作,如果那樣的話我就就卡在了下載圖片那個地方,傻住了。我們希望我們只要一啟動加載的命令,回來的時候主線程能夠繼續進行下去。

我們可以啟動一個協程去下載,主進程依然在運行。有點類似多線程,不同的是,實際上它還是在一個線程里面,所以我們是在主線程中開啟一段邏輯來進行處理,來協同當前程序的執行,還是在主線程里面。

1:在主進程中開啟另外一段邏輯處理,來協同當前程序的執行,但與多線程不同都是在主線程里面執行的,而且原來的進程依然進行下去,

2:通過StartCoroutine方法來啟動一個協程,StartCoroutine是MonoBehaviour的一個方法,該方法可以啟動一個協程,每個協程都有一個入口函數,協程必須要是一個IEnumerator 作為返回值的方法(入口函數);

3:協同程序可以使用yield 關鍵字來中斷協同程序;

4:協程也可以啟動一個協程;

5:WaitForSeconds(): 等待多長時間后中斷協程;

 

 

協程實例

1.創建Unity項目和文件目錄,保存場景

2.創建一個空節點game,再創建一個腳本game掛載在gamej節點下

 打開game腳本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class game : MonoBehaviour {

    private int level=3;
    // Use this for initialization
    void Start () {
        //啟動一個協程,必須是繼承自MonoBehaviour才能使用
        this.StartCoroutine(this.con_entry());
        
        //主線程依然在執行
        //...
    }

    //協程和主線程是在同一個線程里面的,不會有什么線程切換
    //協程的入口函數
    IEnumerator con_entry(){
        //協程的代碼
        Debug.Log("con_entry run!!");
        Debug.Log("level:" + this.level);//也能夠拿到this的變量
        this.StartCoroutine(this.con_other());//再開啟一個協程con_other
        //end


        //使用yield中斷協程程序,就是讓它停下來
        yield return null;


        //協程結束以后的代碼,比如去網上撈一個什么東西,下載圖片之類的,撈完之后的操作
        //end
    }

    IEnumerator con_other()
    {
        Debug.Log("con_other");
        this.StartCoroutine(this.con_entry_params(3));//再開啟一個協程con_entry_params,且傳遞參數
        
        yield return null;
    }

    IEnumerator con_entry_params(int a)
    {
        Debug.Log("con_entry_params: " + a);

        yield return new WaitForSeconds(3);//設置3秒中之后才中斷協程

        Debug.Log("after WaitForSeconds send");// 處理協程結束的邏輯
    }


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

 3.運行結果

 

 

 

多線程

我們的操作系統是多任務操作系統,實際上只有一個cpu是怎樣做到多任務的呢?就是通過線程切換來完成的,每個任務可以看成是一個進程或者線程,多任務多線程就是讓cpu在線程里面不斷地切換,如果切換的頻率足夠快,人們就會認為多個任務是同時在執行的。

例子:一邊編輯文本,一邊播MP3,播MP3也就是解碼一段3秒鍾的音樂,只需要消耗CPU0.01秒,也就是說剩下的2.99秒CPU都是空閑的,所以才提出多線程的解決方案,充分利用CPU資源。

游戲一般都是有一個主線程,是處理游戲的主要事情,但是有些事情我們不希望在主線程里面處理,

比如有一項工作特別耗時,下載一個資源文件,如果把它放在主線程里面等待下載完成的那段時間就卡住了,游戲就卡住了,要等10分鍾下載好后主線程才會恢復,這是無法忍受的。

於是我們的做法就是開啟另外一個線程,然后再去下載,下載過程中通知主線程當前下載的量,主線程繼續執行並更新下載的數據。

多線程的好處是:1.現在的手機電腦的CPU都是多個核的,每個核是可以獨立調度一個線程的,本來游戲只在一個核上運行,如果有了一個復雜的計算,把它放在另外一個線程里面,

         如果是運行在雙核上面,那就有兩個核來運行游戲,一個核來跑主線程,一個核來跑復雜的運算,這樣子,基本上CPU的利用率就能上來,就能幫助我們快速處理復雜運算,這個叫做多核優化。

        2.不至於卡死主線程,能夠讓主線程獲得一個更好的處理方式和更好的用戶體驗

        3.CPU有多個核,有幾個核就可以調度幾個線程,比如雙核的就可以一次性調度兩個線程主線程在跑,復雜線程也在跑,都不耽誤。

1: using System.Thread;

2: 創建一個線程: Thread r = new Thread(callback);  r.start(); 啟動運行線程;

3: 線程回掉函數 void run() {}

4: 多個線程訪問同一個數據的時候,會發生”沖突”,需要線程安全的方式來訪問;

5: 線程鎖是在訪問公共數據的時候,先去獲得這個鎖,沒有獲得鎖的線程將會被掛起,指導這個鎖被釋放。

6: public static Object  o = new Object(); lock(o) {線程安全的執行代碼};

7: 線程休眠: Thread.Sleep(單位為ms);

8: 多線程之間要避免死鎖;

 

 

多線程案例

1.創建Unity項目和文件目錄,保存場景

2.創建一個空節點game,再創建一個腳本game掛載在gamej節點下

 打開game腳本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;//多線程要用到的庫,多線程對象所在的名字空間

public class game : MonoBehaviour {


    // Use this for initialization
    void Start () {
      
        //創建一個線程t1,關聯入口函數
        Thread t1 = new Thread(this.thread_run);//不是像協程那樣直接運行
        t1.Start();//這里才開始執行,開啟線程

        //再創建一個線程t2
        Thread t2 = new Thread(this.thread_run2);
        t2.Start();

    }

    //線程t1的入口函數
    void thread_run(){
        int i = 0;
        while (i < 10){//打印10次
            Debug.Log("thread_run");
            i++;
            Thread.Sleep(3000);//讓線程休息3秒鍾,有點像定時器,里面參數是毫秒為單位的
        }
    }

    //線程t2的入口函數
    void thread_run2(){
        int i = 0;
        while (i < 10){//打印10次
            Debug.Log("thread_run2");
            i++;
            Thread.Sleep(3000);
        }
    }

    // Update is called once per frame
    void Update () {
        //Debug.Log("zhu xian cheng");//主線程也在運行
    }
}

3.運行結果

 

 

 

線程鎖

線程鎖,兩個線程是獨立的,假設他們要訪問同一個變量,訪問公共的資源(全局變量),就需要加鎖,才能保證數據訪問的安全

兩個人都要訪問共用的資源,怎樣讓他們不沖突,如我正在改,你也在改,那到底是改誰的,哪個邏輯是對的。或者我在讀,你在改,我讀到的到底是你改之前的還是你改之后的,就會有很多不確定性。

所以,當兩個線程來訪問公共資源的時候,可以采用鎖的結構來解決這個問題,就是說你進來之前先拉一下鎖,如果你拿到了這個鎖的權利,這時候就你進去訪問,其他的線程就會掛起等待,等到你把這個鎖釋放出來,再繼續。

 

 

線程鎖案例

1.創建Unity項目和文件目錄,保存場景

2.創建一個空節點game,再創建一個腳本game掛載在gamej節點下

 打開game腳本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;//多線程要用到的庫,多線程對象所在的名字空間

public class game : MonoBehaviour {

    // 定義一個靜態全局的對象,這個就是鎖了
    public static Object o = new Object();

    //定義一個靜態變量可以被全局訪問
    public static int g_a = 3;

    // Use this for initialization
    void Start () {
      
        //創建一個線程t1,關聯入口函數
        Thread t1 = new Thread(this.thread_run);//不是像協程那樣直接運行
        t1.Start();//這里才開始執行,開啟線程

        //再創建一個線程t2
        Thread t2 = new Thread(this.thread_run2);
        t2.Start();

    }

    //線程t1的入口函數
    void thread_run(){
        Debug.Log("thread_run wait for lock...");
        lock (o) // 如果沒有獲得這個鎖,那么線程就會掛起等待,只有等他獲得了這個鎖才會繼續往下執行,這個o就是一個鎖
        {  // 你獲取到了這個鎖,開始操作
            Debug.Log("thread_run get lock");
            g_a = 10;
            Debug.Log("g_a = " + g_a);
            Thread.Sleep(20000);//故意讓它睡20秒便於觀察
            Debug.Log("thread_run release lock");
        } // 括號結束了以后,你就釋放了這個鎖,其他的線程就有機會獲得這個鎖
    
    }

    //線程t2的入口函數
    void thread_run2(){
       Debug.Log("thread_run2 wait for lock...");
       lock (o) // 如果沒有獲得這個鎖,那么線程就會掛起等待,只有等他獲得了這個鎖才會繼續往下執行,這個o就是一個鎖
       {  // 你獲取到了這個鎖,開始操作
           Debug.Log("thread_run2 get lock");
           g_a = 10;
           Debug.Log("g_a = " + g_a);
           Thread.Sleep(20000);//故意讓它睡20秒便於觀察
           Debug.Log("thread_run2 release lock");
       } // 括號結束了以后,你就釋放了這個鎖,其他的線程就有機會獲得這個鎖
    
    }

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

    }
}

3.運行結果

 

 

 

死鎖

死鎖,線程A拿了鎖A,在等待鎖B,線程B拿了鎖B,在等待鎖A,兩個線程彼此都拿了一個鎖,我在等你,你在等我,在等待彼此的對方的鎖不讓步,這就是死鎖。

死鎖后很麻煩,要重啟Unity。

 

 

死鎖案例

1.創建Unity項目和文件目錄,保存場景

2.創建一個空節點game,再創建一個腳本game掛載在gamej節點下

 打開game腳本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;//多線程要用到的庫,多線程對象所在的名字空間

public class game : MonoBehaviour {

    /*
     * 死鎖: A線程拿了鎖A,在等拿鎖B;
     * B線程拿了鎖B,在等拿鎖A;
     * 如果說你一個線程同時要獲取多個鎖的時候,為了比避免死鎖,
     * 你要按照相同的次序來拿;
     */

    //定義兩個鎖,分別為鎖A和鎖B
    public static Object A = new Object();
    public static Object B = new Object();
    

    // Use this for initialization
    void Start () {
      
        //創建一個線程t1,關聯入口函數
        Thread t1 = new Thread(this.thread_run);//不是像協程那樣直接運行
        t1.Start();//這里才開始執行,開啟線程

        //再創建一個線程t2
        Thread t2 = new Thread(this.thread_run2);
        t2.Start();

    }

    //線程t1的入口函數
    void thread_run(){
        lock (A)
        {
            Thread.Sleep(3000);//先休息3秒鍾,保證線程2拿到鎖B
            Debug.Log("t1 wait for Lock B");
            lock (B)
            {
                Debug.Log("t1 run");
            }
            Debug.Log("t1 release B");
        }
        Debug.Log("t1 release A");
    }

    //線程t2的入口函數
    void thread_run2(){
        //死鎖語句
        /*lock (B)
        {
            Thread.Sleep(3000);//先休息3秒鍾,保證線程1拿到鎖A
            Debug.Log("wait for Lock A");
            lock (A)
            {
                Debug.Log("run");
            }
            Debug.Log("release A");
        }
        Debug.Log("release B");
        */

        //解決死鎖的方法
        lock (A)
        {
            Thread.Sleep(3000);
            Debug.Log("t2 wait for Lock B");
            lock (B)
            {
                Debug.Log("t2 run");
            }
            Debug.Log("t2 release B");
        }
        Debug.Log("t2 release A");
    }

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

    }
}

3.運行結果

死鎖

 

死鎖解決

 

 

 

WWW

Unity中用來處理HTTP協議相關的,有HTTPget和HTTPpost,WWW就是HTTP封裝的一個對象

1: WWW w = new WWW(url);
2: WWW(url)會在后台下載url數據;
3: yeild return w來在協程里等待下載完成;
4: 完成后可以繼續處理;

5.還可以獲取網頁其他的內容,數據,表單,圖片等等,可以點擊WWW類點F12查看。

 

 

WWW案例

1.創建Unity項目和文件目錄,保存場景

2.創建一個空節點game,再創建一個腳本game掛載在gamej節點下

 打開game腳本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;//多線程要用到的庫,多線程對象所在的名字空間

public class game : MonoBehaviour {


    // Use this for initialization
    void Start () {
      
        //創建一個線程t1,關聯入口函數
        Thread t1 = new Thread(this.thread_run);//不是像協程那樣直接運行
        t1.Start();//這里才開始執行,開啟線程

        //再創建一個線程t2
        Thread t2 = new Thread(this.thread_run2);
        t2.Start();

        // 啟動一個協程去加載網絡數據---->撈百度網頁
        this.StartCoroutine(this.http_get_baidu());
        Debug.Log("do other");//可以馬上做其他的事情,抓百度網站的事情交給協程去做
    }

    IEnumerator http_get_baidu()
    {
        // 完成unity http請求和協議;
        WWW w = new WWW("www.baidu.com");
        // 網絡需要抓取時間,在后台抓取網絡數據,一段時間以后,才能完整的抓完整個數據;
        yield return w;

        // 抓取完成網絡數據
        Debug.Log(w.text);
        // end 
    }

    //線程t1的入口函數
    void thread_run(){
       
    }

    //線程t2的入口函數
    void thread_run2(){
     
    }

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

    }
}

3.運行結果

 


免責聲明!

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



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