Unity 中 使用c#线程

使用条件

  天下没有免费的午餐,在我使用unity的那一刻,我就感觉到不自在,因为开源所以不知道底层实现,如果只是简单的做点简单游戏,那就无所谓的了,但真正用到实际地方的时候,就会发现一个挨着一个坑,然后你就跟着unity做各种妥协。如果开发中需要使用网络等等涉及到多线程的地方,就会用到c#的多线程,注意不是unity的协程,你要做的妥协参考下面(网友整理,我没去搜索)的:

1. 变量(都能指向相同的内存地址)都是共享的

2. 不是UnityEngine的API能在分线程运行

3. UnityEngine定义的基本结构(int,float,Struct定义的数据类型)可以在分线程计算,如 Vector3(Struct)可以 , 但Texture2d(class,根父类为Object)不可以。

4  UnityEngine定义的基本类型的函数可以在分线程运行,类的函数不能在分线程运行

 

unity设计之初应该就是一个单线程,不允许在另外的线程中进行渲染等等的工作可以理解,不然又要增加很多机制去处理这个问题,会给新来的人徒增很多烦恼,比如说我:

//class
public class NIDataManager: MonoBehaviour
{
    public static NIDataManager GetInstance()
    {
    }
}
/*thread*/
    private void RequestEvevtThread()
    {
        while (!m_IsExitThread)
        {
            /*wait set event*/
            m_EventWait.WaitOne(-1);

            /*reference SendRequestToService,error ,using subclass of MonoBehaviour in thread*/
            if (NIDataManager.GetInstance().isServiceOpen())  
            {
                if (0 == ServiceInterface.sendRequest((int)(m_EventParam.ActionType), m_EventParam.DurationTime, ref m_EventParam.Response))
                {
                    if (m_EventParam.DelegateCallback != null)
                    {
                        m_EventParam.DelegateCallback(m_EventParam.Response);
                    }
                }
                else
                {
                    // Debug.Log("sendRequest false");
                    continue;
                }
            }

            m_isProcessing = false;
        }
    }

 

如果你也这样干,恭喜你,会得到错误:CompareBaseObjectsInternal can only be called from the main thread.

关于这个错误的一些分析可以参考:http://forum.unity3d.com/threads/comparebaseobjectsinternal-error.184069/

 

委托是个坑

 

第一版思路

游戏通过向体感引擎申请动作识别结果,但是这个结果在处理结束之前,需要堵塞线程,交互设计本身是有问题的,如果在主线程做肯定不可能。OK ,一般情况下,游戏中角色申请完事件之后需要根据结果对人物的状态,如位置等等的进行修改,这其中肯定会涉及到unity的API ,看到这个地方之后我的第一个思路代码如下:

 

image

using System;
using System.Collections.Generic;
using System.Threading;


public class NIEventThread
{
    /*事件处理后的委托*/
    public delegate void EventCallbackMethod(int bSuccess);

    /*Event申请使用的参数*/
    public struct stEventParam
    {
        public int ActionType;                //动作类型
        public int DurationTime;              //限定时间
        public EventCallbackMethod DelegateCallback;  //处理后的结果返回

        public stEventParam(int type, int time, EventCallbackMethod func)
        {
            ActionType = type;
            DurationTime = time;
            DelegateCallback = func;
        }
    }

    /*线程中申请需要的参数*/
    private stEventParam m_EventParam = new stEventParam();
    /*线程控制量,等待申请事件*/
    private AutoResetEvent m_WaitEvent = new AutoResetEvent(false);
    /*线程结束控制*/
    private bool m_IsExitThread = false;
    /*线程,需要设置为后台线程*/
    private Thread m_EventThread = null;

    public Thread GetThread
    {
        get { return m_EventThread; }
    }

    /*初始化的时候就打开线程*/
    public NIEventThread()
    {
        StartThread();
    }

    ~NIEventThread()
    {
        EndThread();
    }

    /*创建和开启线程*/
    private void StartThread()
    {
        m_EventThread = new Thread(this.RequestEvevtThread);
        m_EventThread.IsBackground = true;
        m_EventThread.Start();
    }

    /*结束线程*/
    private void EndThread()
    {
        m_IsExitThread = false;
    }

    /* 请求事件*/
    public void RequestEvent(stEventParam param)
    {
        m_EventParam = param;
        m_WaitEvent.Set();
    }

    /*线程处理方法*/
    public void RequestEvevtThread()
    {
        while (!m_IsExitThread)
        {
            /*等待事件激活*/
            m_WaitEvent.WaitOne(-1);

            Console.WriteLine("等你好久了" + "duration:" + m_EventParam.DurationTime);

            if (m_EventParam.DelegateCallback != null)
            {
                m_EventParam.DelegateCallback(1);
            }
        }
    }
}

unity中的调用:

// Update is called once per frame
    void Update ()
    {
        if (Input.GetKey(KeyCode.Space))
        {
            //申请事件
            t.RequestEvent(new stEventParam(3, 256,test));
        }
    }

    void test(int t)
    {
        //掉用unity API
        transform.Rotate(new Vector3(30,0,0));
    }

处理结果:

产生错误“InternalGetTransform can only be called from the main thread.”

也就是说:这个委托还在分支线程中执行,而不是在代码所在的主线程中执行,具体原因另外一篇文章讲解,其实在c#中控件也不允许在另外一个线程中进行控制,但是微软提供了方法,unity没提供或者说本身就不建议这么干。

 

怎么破?我这里给出我自己的思路,增加队列,在时间请求线程中先队列中增加要处理的事件,在unity主线程中取队列中的参数进行处理,OK ,看代码:

队列:

image

public sealed class RequestMessageQueue
{
    private readonly static RequestMessageQueue  mInstance = new RequestMessageQueue();
    public static RequestMessageQueue GetInstance()
    {
        return mInstance;
    }

    public  void EnQueue(stEventResult st)
    {
        m_Event.Enqueue(st);
    }

    public stEventResult DeQueue()
    {
        return m_Event.Dequeue();
    }

    public int Count
    {
        get { return m_Event.Count; }
    }

    private RequestMessageQueue()
    {
        m_Event = new Queue<stEventResult>() ;
    }

    Queue<stEventResult> m_Event;
}

 

unity中事件处理

public class RequestMessageHandle : MonoBehaviour
{

    // Use this for initialization
    void Start () 
    {
    
    }
    
    // Update is called once per frame
    void Update () 
    {
        while (RequestMessageQueue.GetInstance().Count != 0)
        {
            stEventResult st = RequestMessageQueue.GetInstance().DeQueue();
            st.DelegateCallback(st.result);
        }
    }
}

 

再来看看线程中调用:

/*线程处理方法*/
    public void RequestEvevtThread()
    {
        while (!m_IsExitThread)
        {
            /*等待事件激活*/
            m_WaitEvent.WaitOne(-1);

            Console.WriteLine("等你好久了" + "duration:" + m_EventParam.DurationTime);

            if (m_EventParam.DelegateCallback != null)
            {
                //m_EventParam.DelegateCallback(1);
                 RequestMessageQueue.GetInstance().EnQueue(new stEventResult(m_EventParam.ActionType,1,m_EventParam.DelegateCallback));
            }
        }
    }

 

修改完之后,unity中原先的逻辑不需要更该,就可以看到测试中按键点击后,物体的旋转。

posted @ 2014-10-16 19:26  RubbyZhang  阅读(10432)  评论(0编辑  收藏  举报