简体   繁体   中英

Two thread for receive and process data with high speed rate from udp socket

in my application, data are received from UDP socket with high speed rate(450 Mbps - each udp packet size is 1Kb). I need two threads. First thread receives data form socket and writes data in a buffer(byte[]); Second thread reads data from buffer and processes them. I need to receive and process them in the real time mode. I use a variable "TableCounter" to protect two threads and use "SetThreadAffinityMask" for both threads and set to two different threads of CPU.

I implement this senario in c++ and it's fine with high speed. but in c#, my application is slow and i lost some packet(i have counter in each packet and i check it). How can I resolve the speed and data lost problems? which type of buffer is good for this senario( two threads access to buffer)? please help me.

/////////////////////////////////////
first thread
/////////////////////////////////////
Datain = new Thread(new ThreadStart(readDataUDPClient));
Datain.IsBackground = true;
Datain.Priority = ThreadPriority.Highest;
Datain.Start();

/////////////////////////////////////
public void readDataUDPClient()
        {

            var ptr = GetCurrentThread();
            SetThreadAffinityMask(ptr, new IntPtr(0x0002));

            UdpClient client = new UdpClient(20000);
            client.EnableBroadcast = true;
            IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 20000);
            client.Client.ReceiveBufferSize = 900000000;

            while (true)
            {
                tdata = new byte[1024];
                tdata = client.Receive(ref anyIP);

                // check counter

                Array.Copy(tdata , 0, Table, TableCounter, 1024);
                TableCounter += 1024;

                if (TableCounter == TableSize)
                {
                    TableCounter = 0;
                    Cycle++;
                }

                if (TableCounter == 10240) 
                {
                    waitHandle.Set(); // set event for start second thread
                }
            }
        }

and

/////////////////////////////////////
Second thread
/////////////////////////////////////
public void processData()
    {
    var ptr = GetCurrentThread();
            SetThreadAffinityMask(ptr, new IntPtr(0x0004));
    int Counter = 0;
    waitHandle.WaitOne();

    while (true)
            {
        if ((Counter < TableCounter) && (Counter <TableSize)) {
            // Read from Table
            // process data
            counter++; // local counter
        }if ((Counter ==TableSize)) {
            // Read from Table
            // process data
            counter=0; // local counter
        } else {
            Thread.Sleep(1); // or break for set another event
        }
    }
}

FWIW, if data loss is a concern then UDP might not be your best choice.

A brief inspection of the receive code suggests that you don't need to "pre-allocate" tdata. C# doesn't just allocate that memory it also zero's it, each iteration. You are paying the cost to do this, then throwing it away because Receive is returning a (different) buffer. Try something like:

var tdata = client.Receive(ref anyIP);

Allocating tdata and the Array.Copy would be where your time is being spent. A "more C#'ish" approach would be to declare Table as a list or array like so:

List<byte[]> Table = new List<byte[]>(10);
....
Table[ix] = client.Receive(ref anyIP);
ix++
if (ix > 10) ... do something cycle, etc.

This gets rid of tdata entirely and assigns your return value to the appropriate index in Table. This would of course necessitate changing your second threads function to using the list of buffers structure. These changes reduce two 1024 byte copy operations (one for the initialize, one for the Array.Copy), to setting one pointer.

You also need to be carefully that your first (producing) thread doesn't outpace your second (consuming) thread. I assume that in production your second thread will do something more that go through the buffer 1 byte at time (your second thread loops 1024 times for each time you go through your first thread's loop.


Additional content in response to comments

Two questions came up in the comments. It appears that the original question was a simplified version of the actual problem. However I would recommend the same approach. "Harvest"/Read the data in a tight loop into a pre-allocated array of blocks. For example a 100 element array of n bytes per element. The number of bytes returned is controlled by the UdpClient.Recieve method and will be equal to the data portion of the UDP packet sent. Since the initial array is just an array of pointers (to byte[]) you should make it large enough to handle a suitable number of backlogged data packets. If this is running on a PC with reasonable amount of memory start with something like List(16000), then adjust the starting value up or down depending on how quickly your consumer threads process the data.

If you think that the UdpClientRecieve method is a performance bottleneck, create a Diagnostics.Stopwatch object and wrap the call with a stopwatch.Start/Stopwatch.Stop call. Keep track of those calls to see what the actual time cost is; example:

    var tracker = List<double>();
    var stopwatch = new System.Diagnostics.Stopwatch();

    // do some stuff, then enter while (true) loop


        stopwatch.Reset();
        client.Receive(ref anyIP);
        stopwatch.Stop();
        tracker.Add(stopwatch.Elapsed.TotalMilliseconds());

You can write the contents of tracker to a file at the end of a run or use the debugger (and maybe linq) to look at the data. This will tell you exactly how much time is spent in Receive and if you are hitting any "pauses" from say the GC.

The second issue seems to be with the amount of work you are trying to do in your consumer thread (the thread processing the data). If you are doing a lot of work for each data block read, then you might want to consider spawning multiple threads that each work on a different block. You could have a "Dispatcher" thread that reads a block and sends it to one of a pool of worker threads. The Dispatcher block would watch the growth of Table and do whatever is considered appropriate if it gets too "large".

You might want to consider breaking this into multiple questions as SO is more tuned to tightly targeted questions and answers. First try to establish if Receive really is the problem by comparing the actual times to the required or expected times. How many UDP packets per second do you need to handle? How many are being handled? etc.

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