【Unity】線程安全的消息傳輸機制,仿照Cocos實現

近期用到了網絡通信的方法,雖然unity可以用協程來實現異步操作,不過坑爹的隊友不會用,他用的是傳統的開線程的方法,這樣就會出現線程安全的問題,然後現有的消息通信機制無法滿足需求了,就得改了。還好我機智的看過Cocos2dx中消息機制的實現原理,順手改了一下,下面貼源碼:(源碼後有解釋)

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/**
 * 消息派發類 
 */
namespace Assets.TGUI.UIScript
{
    public class CDispatcher
    {
        //單例
        public static readonly CDispatcher Instance = new CDispatcher();

        //消息委託函數
        public delegate void noticeDelegate(object[] data);

        /// <summary>
        /// 消息Key
        /// </summary>
        public struct Key
        {
            public string name;
            public object target;

            public Key(string _name, object _target)
            {
                name = _name;
                target = _target;
            }
        }

        //存儲消息列表
        private Dictionary<Key, noticeDelegate> m_noticeDict;

        private List<KeyValuePair<Key, object[]>> m_noticeRequest;

        private List<KeyValuePair<Key, object[]>> m_tempRequest;

        private CDispatcher()
        {
            m_noticeDict = new Dictionary<Key, noticeDelegate>();
            m_noticeRequest = new List<KeyValuePair<Key, object[]>>();
            m_tempRequest = new List<KeyValuePair<Key, object[]>>();
        }

        /// <summary>
        /// 循環遍歷 執行消息請求
        /// </summary>
        public IEnumerator Dispatcher()
        {
            Debug.Log("MSG: Start Dispatcher");
            do
            {
                if (m_tempRequest.Count != 0)
                {
                    lock (m_tempRequest)
                    {
                        foreach (var item in m_tempRequest)
                        {
                            m_noticeRequest.Add(new KeyValuePair<Key, object[]>(item.Key, item.Value));
                        }
                        m_tempRequest.Clear();
                    }

                    foreach (var item in m_noticeRequest)
                    {
                        if (m_noticeDict.ContainsKey(item.Key))
                        {
                            m_noticeDict[item.Key](item.Value);
                        }
                    }

                    m_noticeRequest.Clear();
                }
                yield return new WaitForFixedUpdate();
            } while (true);
        }

        /**
         * 發送消息
         * @param notifyName 消息類型
         * @param data 攜帶參數
         */
        public void sendNotification(string noticeName, object[] data = null)
        {
            sendNotification(noticeName, null, data);
        }

        /// <summary>
        /// 發送消息
        /// </summary>
        /// <param name="noticeName"></param>
        /// <param name="target"></param>
        /// <param name="data"></param>
        public void sendNotification(string noticeName, object target, object[] data)
        {
            m_tempRequest.Add(new KeyValuePair<Key, object[]>(new Key(noticeName, target), data));
        }

        /// <summary>
        /// 發送消息 附帶的數據是發送者本身的對象
        /// </summary>
        /// <param name="noticeName"></param>
        /// <param name="target"></param>
        /// <param name="source"></param>
        public void sendNotification(string noticeName, object target, object source)
        {
            sendNotification(noticeName, target, new object[] { source });
        }

        /// <summary>
        /// 獲取到源類型變量
        /// (與sendNotification(string noticeName, 
        /// object target, object source)對應)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <returns></returns>

        public T GetSourceObject<T>(object[] data)
        {
            return (T)data[0];
        }

        public void addObserver(string noticeName, noticeDelegate notice)
        {
            addObserver(noticeName, null, notice);
        }

        public void addObserver(string noticeName, object target, noticeDelegate notice)
        {
            Key key = new Key(noticeName, target);
            if (!m_noticeDict.ContainsKey(key))
            {
                m_noticeDict.Add(key, notice);
            }
        }

        public void RemoveObserver(string noticeName)
        {
            RemoveObserver(noticeName, null);
        }

        public void RemoveObserver(string noticeName, object target)
        {
            Key key = new Key(noticeName, target);

            if (!m_noticeDict.ContainsKey(key))
            {
                m_noticeDict.Remove(key);
            }
        }
    }
}

方法稍微有點略多,但是值得關注的方法就是下面幾個

IEnumerator Dispatcher()
public void sendNotification(...)
public void addObserver(...)
public void RemoveObserver(...)


首先來說第一個,第一個是消息循環遍歷的方法,需要在遊戲主對象中的start方法中使用協程開啓消息循環遍歷,寫法如下:

        //開啓消息循環
        StartCoroutine(CDispatcher.Instance.Dispatcher());

然後和Cocos2dx一樣,添加觀察者

CDispatcher.Instance.addObserver("消息名字", [目標對象], 方法);

然後發送消息

CDispatcher.Instance.sendNotification("消息名字",[目標對象],[附加數據]);


下面是一個完整的使用例子:

protected void Start()
    {
        //開啓消息循環
        StartCoroutine(CDispatcher.Instance.Dispatcher());
        CDispatcher.Instance.addObserver("我要睡覺", x => Debug.Log("睡覺"));
        CDispatcher.Instance.sendNotification("我要睡覺");
}

最後當然輸出“睡覺”咯


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章