简体   繁体   中英

Processing received UDP data in another thread in C#

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.

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();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM