Processing received UDP data in another thread in

2019-06-13 13:16发布

I have a class which contains a method for receiving UDP data in a separate thread. I do this to avoid the main application (which is running in Unity3D) from stalling.

I need to pass the data that is received in the separate thread to another class, which runs on the original thread and, as such, is able to interact with Unity3D.

Here is roughly what the UDPReceiver looks like:

public class UDPReciever {

    //...

    public UDPReciever() {
        m_Port = 12345;
        m_Worker = new Thread(new ThreadStart(recvData));
        m_Worker.IsBackground = true;
        m_Worker.Start();
    }

    void recvData() {
        m_UDPClient = new UdpClient(m_Port);
        while (true) {
            try {
                IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 0);
                byte[] data = (m_UDPClient.Receive(ref anyIP));  

                // TODO: Hand 'data' to NetworkController class (running in the original thread) for processing

            } catch (Exception err) {
                    print(err.ToString());
            }
        }
    }   

}

This is roughly what the NetworkController class needs to look like. Ideally the "OnNewData" method would be called every time a new packet is received with the data passed as an argument.

public class NetworkController {

    //...

    void OnNewData(pData) {
        // Process the data in this thread
    }

}

How would I go about achieving this? Thanks in advance.

1条回答
Ridiculous、
2楼-- · 2019-06-13 13:39

Here is how it could be done (not tested):

public class Dispatcher : MonoBehaviour
{       
    private static readonly BlockingCollection<Action> tasks = new BlockingCollection<Action>();

    public static Dispatcher Instance = null;

    static Dispatcher()
    {
        Instance = new Dispatcher();
    }

    private Dispatcher()
    {
    }

    public void InvokeLater(Action task)
    {
        tasks.Add(task);
    }

    void FixedUpdate()
    {
        if (tasks.Count > 0)
        {
            foreach (Action task in tasks.GetConsumingEnumerable())
            {
                task();
            }
        }
    }
}
...
NetworkController networkControllerInstance;

void recvData()
{
    m_UDPClient = new UdpClient(m_Port);
    while (true)
    {
        try
        {
            IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 0);
            byte[] data = (m_UDPClient.Receive(ref anyIP));  

            Dispatcher.Instance.InvokeLater(() => networkControllerInstance.OnNewData(data));
        }
        catch (Exception err)
        {
            print(err.ToString());
        }
    }
}

EDIT:

A version that should be compliant with .Net 3.5:

public class Dispatcher : MonoBehaviour
{       
    private static readonly Queue<Action> tasks = new Queue<Action>();

    public static Dispatcher Instance = null;

    static Dispatcher()
    {
        Instance = new Dispatcher();
    }

    private Dispatcher()
    {
    }

    public void InvokeLater(Action task)
    {
        lock (tasks)
        {
            tasks.Enqueue(task);
        }
    }

    void FixedUpdate()
    {
        while (tasks.Count > 0)
        {
            Action task = null;

            lock (tasks)
            {
                if (tasks.Count > 0)
                {
                    task = tasks.Dequeue();
                }
            }

            task();
        }
    }
}

EDIT 2:

if you want to avoid freezing the main thread during a too long period:

void FixedUpdate()
{
    if (tasks.Count != 0)
    {
        Action task = null;

        lock (tasks)
        {
            if (tasks.Count != 0)
            {
                task = tasks.Dequeue();
            }
        }

        task();
    }
}
查看更多
登录 后发表回答