代码改变世界

unity 多线程

2015-07-13 21:49  fat___lin  阅读(1442)  评论(0编辑  收藏  举报

对于客户端来说,好的用户体验,需要保持一个快速响应的用户界面。于是便要求:网络请求、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();

        }
    }
View Code

详见: 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/  进行学习