Unity除了一些基本的數據類型,幾乎所有的API都不能在非unity線程中調用,如果項目中有一段很耗時操作,unity可能會出現“假死”。如果這段操作是和unity無關的,我們可以把這個耗時的操作放到子線程中去運行,防止unity假死提高性能,如下麵這個偽代碼 必須保證LoadLocalFile( ...
Unity除了一些基本的數據類型,幾乎所有的API都不能在非unity線程中調用,如果項目中有一段很耗時操作,unity可能會出現“假死”。如果這段操作是和unity無關的,我們可以把這個耗時的操作放到子線程中去運行,防止unity假死提高性能,如下麵這個偽代碼
Function { //這個函數會進行大量文件讀寫操作 LoadLocalFile(); //這個函數是unity函數 UnityFunction(); }
必須保證LoadLocalFile() 不“假死”。怎麼做呢?只要把Function放到多線程中,UnityFunction()回到主線程即可
網上的做法一般都是借鑒Loom,摘錄一個腳本
1 using UnityEngine; 2 using System.Collections; 3 using System.Collections.Generic; 4 using System; 5 using System.Threading; 6 using System.Linq; 7 8 public class Loom :MonoBehaviour 9 { 10 public static int maxThreads = 8; 11 static int numThreads; 12 13 private static Loom _current; 14 //private int _count; 15 public static Loom Current 16 { 17 get 18 { 19 Initialize(); 20 return _current; 21 } 22 } 23 24 void Awake() 25 { 26 _current = this; 27 initialized = true; 28 } 29 30 static bool initialized; 31 32 public static void Initialize() 33 { 34 if (!initialized) 35 { 36 37 if (!Application.isPlaying) 38 return; 39 initialized = true; 40 var g = new GameObject("Loom"); 41 _current = g.AddComponent<Loom>(); 42 #if !ARTIST_BUILD 43 UnityEngine.Object.DontDestroyOnLoad(g); 44 #endif 45 } 46 47 } 48 public struct NoDelayedQueueItem 49 { 50 public Action<object> action; 51 public object param; 52 } 53 54 private List<NoDelayedQueueItem> _actions = new List<NoDelayedQueueItem>(); 55 public struct DelayedQueueItem 56 { 57 public float time; 58 public Action<object> action; 59 public object param; 60 } 61 private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>(); 62 63 List<DelayedQueueItem> _currentDelayed = new List<DelayedQueueItem>(); 64 65 public static void QueueOnMainThread(Action<object> taction, object tparam) 66 { 67 QueueOnMainThread(taction, tparam, 0f); 68 } 69 public static void QueueOnMainThread(Action<object> taction, object tparam, float time) 70 { 71 if (time != 0) 72 { 73 lock (Current._delayed) 74 { 75 Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = taction, param = tparam }); 76 } 77 } 78 else 79 { 80 lock (Current._actions) 81 { 82 Current._actions.Add(new NoDelayedQueueItem { action = taction, param = tparam }); 83 } 84 } 85 } 86 87 public static Thread RunAsync(Action a) 88 { 89 Initialize(); 90 while (numThreads >= maxThreads) 91 { 92 Thread.Sleep(100); 93 } 94 Interlocked.Increment(ref numThreads); 95 ThreadPool.QueueUserWorkItem(RunAction, a); 96 return null; 97 } 98 99 private static void RunAction(object action) 100 { 101 try 102 { 103 ((Action)action)(); 104 } 105 catch 106 { 107 } 108 finally 109 { 110 Interlocked.Decrement(ref numThreads); 111 } 112 113 } 114 115 116 void OnDisable() 117 { 118 if (_current == this) 119 { 120 121 _current = null; 122 } 123 } 124 125 126 127 // Use this for initialization 128 void Start() 129 { 130 131 } 132 133 List<NoDelayedQueueItem> _currentActions = new List<NoDelayedQueueItem>(); 134 135 // Update is called once per frame 136 void Update() 137 { 138 if (_actions.Count > 0) 139 { 140 lock (_actions) 141 { 142 _currentActions.Clear(); 143 _currentActions.AddRange(_actions); 144 _actions.Clear(); 145 } 146 for (int i = 0; i < _currentActions.Count; i++) 147 { 148 _currentActions[i].action(_currentActions[i].param); 149 } 150 } 151 152 if (_delayed.Count > 0) 153 { 154 lock (_delayed) 155 { 156 _currentDelayed.Clear(); 157 _currentDelayed.AddRange(_delayed.Where(d => d.time <= Time.time)); 158 for (int i = 0; i < _currentDelayed.Count; i++) 159 { 160 _delayed.Remove(_currentDelayed[i]); 161 } 162 } 163 164 for (int i = 0; i < _currentDelayed.Count; i++) 165 { 166 _currentDelayed[i].action(_currentDelayed[i].param); 167 } 168 } 169 } 170 }
使用方式
Function { //非同步在多線程下運行 Loom.RunAsync(() => { //這個函數會進行大量文件讀寫操作 LoadLocalFile(); //回到unity線程繼續運行 Loom.QueueOnMainThread(()=> { //這個函數是unity函數 UnityFunction(); } } }
這個Loom類的大概思路就是把整個代碼塊放多線程中,並把需要在主線程運行的代碼按委托的方式封裝起來保存的list里,Updata函數是unity函數,他會自動每幀執行一次。所以他可以判斷list是否有任務並執行任務,從而實現了回到主線程