Unity主线程和子线程跳转调用(1)
程序员文章站
2022-04-12 08:08:05
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是否有任务并执行任务,从而实现了回到主线程