多線程 概述 單任務處理:一個任務完成後才能進行下一個任務。 多任務處理:CPU分時操作,每個任務看似同時運行。 進程 應用程式的一個運行實例,包含程式所需資源的記憶體區域,是操作系統進行資源分配的單元,進程隔離了正在執行的不同程式。(打開某一個軟體,分配CPU,隔離其他軟體) 優點:進程間相互獨立, ...
多線程
概述
單任務處理:一個任務完成後才能進行下一個任務。
多任務處理:CPU分時操作,每個任務看似同時運行。
進程
應用程式的一個運行實例,包含程式所需資源的記憶體區域,是操作系統進行資源分配的單元,進程隔離了正在執行的不同程式。(打開某一個軟體,分配CPU,隔離其他軟體)
優點:進程間相互獨立,互不影響。
線程
進程中的一個執行單元(進程是程式邊界,要靠線程執行程式,線程指向方法,執行完畢釋放線程),是CPU分配時間片的單位,一個進程可以包含多個線程,且相互獨立,共用當前進程所有資源。
優點:
- 併發執行,合理使用CPU資源。
- 相同程式的線程共用堆記憶體。
缺點:
- 頻繁創建/銷毀線程增加性能開銷。
- 訪問共用資源可能造成衝突。
- 輔助線程不能訪問Unity API。
註意事項:
- Unity的API不能在輔助線程運行。
- Unity定義的基本結構(int,Vector3,Quaternion等)可以在輔助線程計算。
- Unity定義的基本類型的函數可以在分線程運行。
多線程
在單核系統的一個單位時間內,CPU只能運行單個線程,運行順序取決於線程的優先順序。如果在單位時間內線程未能完成執行,系統就會把線程的狀態信息保存到線程的本地存儲器(TLS) 中,以便下次執行時恢復執行。因為切換頻密,所以多線程可被視作同時運行,而實際只是一個假象。
在多核系統的一個單位時間內,進程或線程可以在不同的CPU中運行,使得真正的並行處理。
適用性
耗時的任務,通過多線程可以並行處理。
一個程式完成多個任務,通過多個線程使用多核CPU來處理,可以提升性能。
多線程實現
命名空間:System.Threading;
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Threading; public class ThreadDemo1 : MonoBehaviour { private Thread thread; private void Start01() { //Func1();//同步調用 一次性完成0 1 2 3 4 .... //線程調用 //thread = new Thread(Fun1); //thread.Start(); //睡眠一秒 期間沒有CPU調度 0 睡眠一秒,依次迴圈 1 2 3 4... //thread = new Thread(Fun2); //thread.Start(5); //方法重載 參數object類型 thread = new Thread(Fun4); thread.Start(); //ThreadPool.QueueUserWorkItem(Fun3,null); //線程池開闢的線程無法設置前後臺/優先順序等 //通過線程池開闢線程 不能Start啟用 } //thread.Start() //無參 private void Fun1() { for (int i = 0; i < 30; i++) { signal.WaitOne(3000); //整數 代表最長的等待時間 Thread.Sleep(1000); print(i); } } ////thread.Start(5) //有參 private void Fun2(object o) { int count = (int)o; for (int i = 0; i < count; i++) { Thread.Sleep(1000); print(i); } } /// <summary> /// 對象池 /// </summary> private void Fun3(object o) { //int[] arr = (int[])o; //int count = (int)o; for (int i = 0; i < 5; i++) { Thread.Sleep(1000);//模擬此時操作一分鐘 print(i);//主線程 卡 指的就是生命周期 } } /// <summary> /// 死迴圈 怎麼辦 /// </summary> private void Fun4() { int n = 0; while (true) { Thread.Sleep(1000); print(++n); } } /// <summary> /// 退出線程 /// </summary> private void OnApplicationQuit() { //thread.Abort(); //結束線程 //主線程卡頓的情況 可以開闢新的線程 } /// <summary> /// 信號燈 /// </summary> private ManualResetEvent signal; private void Start() { //信號燈 綠燈行 紅燈停 signal = new ManualResetEvent(true); //true表示綠燈 false 表示紅燈 thread = new Thread(Fun1); thread.Start(); } private void OnGUI() { if (GUILayout.Button("線程暫停")) { signal.Reset(); } if (GUILayout.Button("線程繼續")) { signal.Set(); } } }View Code
Thread
- 創建線程: 創建Thread類的一個對象,分配一個線程工作方法 。
Thread thread = new Thread(工作方法);
- 啟動線程:Start方法。
thread.Start();
- 終止線程:工作方法自然退出、線程終止。
thread. Abort ();
ThreadPool
在頻繁創建和銷毀線程時使用線程池技術,可以有效減少時間以及系統資源的開銷。
ThreadPool.QueueUserWorkItem(工作方法);
前/後臺線程
前臺線程:程式必須等待所有前臺線程結束後才能退出。(只要有一個前臺線程未退出,進程就不會終止!即說的就是程式不會關閉!)[Thread創建的線程預設前臺線程]
後臺線程:程式不考慮後臺線程,後臺線程隨程式退出而結束。[ThreadPool創建的線程預設後臺線程]
備註:Unity程式退出後,前臺線程也隨即關閉。
新建的子線程可以是前臺線程或者後臺線程,前臺線程必須全部執行完,即使主線程關閉掉,這時進程仍然存活。
線程狀態
未啟動狀態 Unstarted:創建線程對象。
運行狀態Running:執行綁定的方法。
等待睡眠阻塞狀態 WaitSleepJoin:暫時停止執行,資源交給其他線程使用。
終止狀態 Stopped:線程銷毀。
using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using UnityEngine.UI; using System.Threading; public class ThreadDemo : MonoBehaviour { private int second = 60; private Text text; private void Start() { text = GetComponent<Text>(); Thread thread = new Thread(Timer); thread.Start(); } private void Update() { if (action!=null) { action(); action = null; } } private void Timer() { while (second>0) { Thread.Sleep(1000);//睡一會 期間沒有CPU的調度 action = () => { second--; text.text = string.Format("{0:d2}:{1:d2}", second / 60, second % 60); }; } } private Action action; }
線程同步
需要同步的原因:
多個線程同一時刻訪問共用資源(線程共用實例變數,靜態變數),由於每個線程都不知道其他線程的操作,結果將產生不可預知的數據損壞。
同步:
線程之間相互等待排隊執行。
如何同步:
將需要同步的代碼用關鍵字lock鎖定,鎖定後該代碼對於線程來講就是獨占使用的。當其他線程試圖進入被鎖定的臨界區時,只能等待解鎖後才可訪問。因此鎖定代碼時是排隊訪問的,所以叫線程同步。
Lock鎖原理:
對象在堆中的分配:實例成員、同步塊索引 (預設索引-1)、類型指針(指向類型對象)。
對象上鎖後,同步索引塊會指向同步塊數組中的一個對象。
當其他對象執行lock的時候會等待該對象同步索引設置為-1。
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Threading; public class Bank { public static int Money = 1; public static object o = new object(); //必須使用引用類型 //優先使用object public static void Get(int val) { lock (o)//-1 0 只有是-1的時候 鎖打開 否則鎖關閉 { //同步塊索引 類型對象指針 //共用讀 獨占寫 //枷鎖(對象) 流程 if (Money >= val) { //線程衝突 0 -1 -2 如何解決???加鎖 Thread.Sleep(1000); Money -= val; Debug.Log("取錢成功!餘額:" + Money); } else { Debug.Log("取錢失敗!餘額:" + Money); } //線程離開代碼塊 索引塊 設置為-1 } } } public class ThreadDemo2 : MonoBehaviour { private void Start() { //Bank.Get(1); //同步調用 排隊 ThreadPool.QueueUserWorkItem(o => { Bank.Get(1); }); ////線程調用(多線程) 不排隊 } }