協程
我們要下載一張圖片,加載一個資源,這個時候一定不是一下子就加載好的,或者說我們不一定要等它下載好了才進行其他操作,如果那樣的話我就就卡在了下載圖片那個地方,傻住了。我們希望我們只要一啟動加載的命令,回來的時候主線程能夠繼續進行下去。
我們可以啟動一個協程去下載,主進程依然在運行。有點類似多線程,不同的是,實際上它還是在一個線程里面,所以我們是在主線程中開啟一段邏輯來進行處理,來協同當前程序的執行,還是在主線程里面。
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.運行結果