Unity中Socket和多线程导致Editor和程序卡住解决

来源:互联网 发布:aws centos 编辑:程序博客网 时间:2024/06/09 14:57

Editor卡住的原因

Unity中使用Socket或者多线程,资源没有正确的释放导致Editor卡住。我们的项目中卡住是由于线程没有正确的关掉,TcpClient没有Close导致的。

避免Socket阻塞

在connect的时候如果连不上会等到Timeout,默认Timeout时间是20s。如果放在主循环中连,就会卡住20s。放在协程中会卡住编辑器。也就是说同步连接的时候是一定会等到Timeout的,程序或者Editor必定会卡20s。所以首先要避免连接的时候长时间阻塞。

所以最佳架构是:使用多线程和异步连接。并且消息的收发都使用队列,每个队列开个线程。发送之前检查是否连接上。

在发送队列中,使用TcpClient的异步连接:BeginConnect,然后AsyncWaitHandle.WaitOne来控制timeout。注意不管有没有连接成功,最后都要调用TcpClient.Close()关掉连接。

 IAsyncResult result = client.BeginConnect(IPAddress.Parse(host), port, null, null);               connected = result.AsyncWaitHandle.WaitOne(1000, false); if (connected ) {      client.EndConnect(result); } else {     client.Close(); }

线程要正确的退出

thread.abort()一般是无法退出线程的(abort调用了,如果thread.isAlive仍然为True就说明没有退出)。所以通常在线程的主循环中自定义控制变量isStop,需要结束的时候将isStop设置为true,退出主循环。并且需要将线程设置为守护线程(C#的isBackground,后台线程),这样主程序退出,就会自动关闭线程。

thread:   while !isStop      dowork

全部代码如下:

public class NetWork : MonoBehaviour {    public static NetWork netWork = null;    public string host = "127.0.0.1";    public int port = 10002;    private TcpClient client;    private bool __connected = false;    public bool connected    {        get { return __connected; }    }    private NetworkStream stream;    private Thread recvProcess = null, sendProcess = null;    //public InputField hostText, portText;    private volatile bool stopSendProcess, stopRecvProcess;    //发送队列    private Queue<BaseStruct> sendQueue;    //半包缓冲区    private string recvData;    private int maxBufferSize = 4096;    private Queue<BaseStruct> recvQueue;    private void Awake()    {        //保证全局唯一        if (netWork != null)        {            DestroyImmediate(gameObject);        } else        {            DontDestroyOnLoad(gameObject);            netWork = this;        }        sendQueue = new Queue<BaseStruct>();        recvQueue = new Queue<BaseStruct>();    }    private void Start()    {        netWork = this;        sendProcess = new Thread(new ThreadStart(SendProcess));        sendProcess.IsBackground = true;        sendProcess.Start();        stopSendProcess = false;        stopRecvProcess = false;    }    public void TryConnect()    {        CreateConnection(host, port);    }    void CreateConnection(string host, int port)    {        try        {            client = new TcpClient();            client.NoDelay = true;            //client.Connect(IPAddress.Parse(host), port);            IAsyncResult result = client.BeginConnect(IPAddress.Parse(host), port, null, null);            __connected = result.AsyncWaitHandle.WaitOne(1000, false);            if (__connected)            {                client.EndConnect(result);            } else            {                client.Close();            }        }        catch (SocketException ex)        {            __connected = false;            Debug.Log("connect error: " + ex.Message);            client.Close();            return;        }        if (__connected)        {            stream = client.GetStream();            if (recvProcess == null)            {                recvProcess = new Thread(new ThreadStart(RecvProcess));                recvProcess.IsBackground = true;                recvProcess.Start();            }        }    }    public static void WriteMessage(BaseStruct item)    {        if (netWork != null)            netWork.Write(item);    }    private void Write(BaseStruct item)    {        sendQueue.Enqueue(item);    }    //数据发送线程,    void SendProcess()    {        while (!stopSendProcess)        {            if (!__connected)            {                TryConnect();            }            while (sendQueue.Count > 0 && __connected)            {                BaseStruct item = sendQueue.Dequeue();                byte[] buffer = System.Text.Encoding.UTF8.GetBytes(item.Serialize());                stream.Write(buffer, 0, buffer.Length);                stream.Flush();            }            Thread.Sleep(5);        }    }    private void RecvProcess()    {        byte[] recvBuf = new byte[maxBufferSize];        while (!stopRecvProcess)        {            int bytesRead = stream.Read(recvBuf, 0, maxBufferSize);            // 解析消息加到 recvQueue        }        //Debug.Log("stopRecvProcess");    }    void OnApplicationQuit()    {        stopSendProcess = true;        stopRecvProcess = true;        if (__connected)        {            __connected = false;            stream.Close();            client.Close();        }        if (recvProcess != null)        {            //recvProcess.Abort();            // 如果没有正确关闭线程,这里的Join就会阻塞,就会卡死编辑器            // recvProcess.Join();            Debug.Log("recvProcess: " + recvProcess.IsAlive);        }        if (sendProcess != null)        {            //sendProcess.Abort();            // sendProcess.Join();            Debug.Log("sendProcess: " + sendProcess.IsAlive);        }    }}
阅读全文
0 0