对于客户端来说,好的用户体验,需要保持一个快速响应的用户界面。于是便要求:网络请求、io操作等 开销比较大的操作必须在后台线程进行,从而避免主线程的ui卡顿。(注:协程也是主线程的一部分,进行大量的io操作也会造成UI卡顿)
在 WPF 中,DispatcherObject 只能通过与它关联的 Dispatcher 进行访问。 例如,后台线程不能更新与 UI 线程中的 Dispatcher 关联的 Button 内容。
在unity4.X中,unity的api 是线程不安全的,只允许主线程进行访问。于是,如果在后台线程中想调用unity的api,就必须从后台线程跨回主线程,进行调用。
然而,unity中并不能直接使用 .net中的 dispatcher,或者像android activity中的runOnUiThread 从后台线程 跨回主线程。
那么,unity 多线程中,主线程和后台线程之间通信要用什么方法去实现呢?
1、通过共享数据:
顺序图大致如下,主线程中用携程循环等待,或者在update中进行检测,直到后台线程完成,修改IsFinish=true,主线程再进行对应的逻辑操作。
详见demo中的CZCoroutine文件夹。(注:CZCoroutine为开发群里好友给的一个自己封装协程管理器,并可支持多线程。)
2、实现一个Loom的管理器,将后台线程中需要在主线程中处理的委托添加到 List<Action> _actions中,并在unity的update中进行处理:
public class Loom : MonoBehaviour { private static Loom _current; public static Loom Current { get { if (_current == null && Application.isPlaying) { var g = GameObject.Find("Loom"); if (g == null) { g = new GameObject("Loom"); } _current = g.GetComponent<Loom>() ?? g.AddComponent<Loom>(); } return _current; } } private void Awake() { if (_current != null && _current != this) { Destroy(gameObject); } else { _current = this; } } private List<Action> _actions = new List<Action>(); public class DelayedQueueItem { public float time; public Action action; public string name; } private List<DelayedQueueItem> _delayed = new List<DelayedQueueItem>(); public static void QueueOnMainThread(Action action, float time, string name) { lock (Current._delayed) { if (Current._delayed.Any(d => d.name == name)) return; QueueOnMainThread(action, time); } } public static void QueueOnMainThread(Action action, string name) { QueueOnMainThread(action, 0, name); } public static void QueueOnMainThread(Action action, float time) { if (time != 0) { lock (Current._delayed) { Current._delayed.Add(new DelayedQueueItem { time = Time.time + time, action = action }); } } else { lock (Current._actions) { Current._actions.Add(action); } } } public static void QueueOnMainThread(Action action) { lock (Current._actions) { Current._actions.Add(action); } } public static void RunAsync(Action a) { var t = new Thread(RunAction); t.Priority = System.Threading.ThreadPriority.Normal; t.Start(a); } private static void RunAction(object action) { ((Action)action)(); } private List<Action> toBeRun = new List<Action>(); private List<DelayedQueueItem> toBeDelayed = new List<DelayedQueueItem>(); private void Update() { //Process the non-delayed actions lock (_actions) { toBeRun.AddRange(_actions); _actions.Clear(); } foreach (var a in toBeRun) { try { a(); } catch (Exception e) { Debug.LogError("Queued Exception: " + e.ToString()); } } toBeRun.Clear(); lock (_delayed) { toBeDelayed.AddRange(_delayed); } foreach (var delayed in toBeDelayed.Where(d => d.time <= Time.time)) { lock (_delayed) { _delayed.Remove(delayed); } try { delayed.action(); } catch (Exception e) { Debug.LogError("Delayed Exception:" + e.ToString()); } } toBeDelayed.Clear(); } }
详见: http://dsqiu.iteye.com/blog/2028503
http://answers.unity3d.com/questions/305882/how-do-i-invoke-functions-on-the-main-thread.html
3、unity多线程插件 Loom
https://www.assetstore.unity3d.com/en/#!/content/7285
这个插件和上面分享的工具类同名,而现在也找不到上面工具类的出处,或许是同个作者,进行了优化,封装拿到unity商店吧。
如果你对C# 中的Thread多线程还不熟悉,推荐 http://www.albahari.com/threading/ 进行学习