简体   繁体   English

两个线程,用于从udp套接字高速接收和处理数据

[英]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). 在我的应用程序中,数据是从UDP套接字以高速速率(450 Mbps-每个udp数据包大小为1Kb)接收的。 I need two threads. 我需要两个线程。 First thread receives data form socket and writes data in a buffer(byte[]); 第一个线程从套接字接收数据,并将数据写入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. 我使用变量“ TableCounter”保护两个线程,并对两个线程使用“ SetThreadAffinityMask”,并将其设置为CPU的两个不同线程。

I implement this senario in c++ and it's fine with high speed. 我在c ++中实现了这个senario,并且速度很快。 but in c#, my application is slow and i lost some packet(i have counter in each packet and i check it). 但是在C#中,我的应用程序运行缓慢,我丢失了一些数据包(我在每个数据包中都有计数器并对其进行检查)。 How can I resolve the speed and data lost problems? 如何解决速度和数据丢失问题? which type of buffer is good for this senario( two threads access to buffer)? 哪种类型的缓冲区适合此senario(两个线程访问缓冲区)? 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. FWIW,如果您担心数据丢失,那么UDP可能不是您的最佳选择。

A brief inspection of the receive code suggests that you don't need to "pre-allocate" tdata. 对接收代码进行简短的检查表明,您不需要“预分配” tdata。 C# doesn't just allocate that memory it also zero's it, each iteration. 在每次迭代中,C#不仅分配该内存,还分配零。 You are paying the cost to do this, then throwing it away because Receive is returning a (different) buffer. 您要为此付出代价,然后将其丢弃,因为Receive会返回一个(不同的)缓冲区。 Try something like: 尝试类似:

var tdata = client.Receive(ref anyIP);

Allocating tdata and the Array.Copy would be where your time is being spent. 分配tdata和Array.Copy将是您花费时间的地方。 A "more C#'ish" approach would be to declare Table as a list or array like so: “更多的C#'ish”方法是将Table声明为列表或数组,如下所示:

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. 这将完全摆脱tdata,并将您的返回值分配给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. 这些更改将两个1024字节的复制操作(一个用于初始化,一个用于Array.Copy)减少为设置一个指针。

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. 我假设在生产中,您的第二个线程将做更多的事情,一次通过缓冲区1个字节(您的第二个线程每次循环执行1024次)。


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. 例如,每个元素n个字节的100个元素数组。 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. 返回的字节数由UdpClient.Recieve方法控制,并且等于发送的UDP数据包的数据部分。 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. 由于初始数组只是指针数组(指向byte []),因此应使其足够大以处理适当数量的积压数据包。 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. 如果此程序在具有合理内存量的PC上运行,并且以诸如List(16000)之类的内容开头,则根据使用者线程处理数据的速度来上下调整起始值。

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. 如果您认为UdpClientRecieve方法是性能瓶颈,请创建Diagnostics.Stopwatch对象,并将该调用包装为stopwatch.Start / Stopwatch.Stop调用。 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. 您可以在运行结束时将跟踪器的内容写入文件中,或者使用调试器(可能是linq)查看数据。 This will tell you exactly how much time is spent in Receive and if you are hitting any "pauses" from say the GC. 这将准确告诉您在接收中花费了多少时间,以及您是否遇到了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". Dispatcher块将监视Table的增长,并在它变得“太大”时做任何被认为适当的事情。

You might want to consider breaking this into multiple questions as SO is more tuned to tightly targeted questions and answers. 您可能需要考虑将其分解为多个问题,因为SO更适合于针对性强的问题和答案。 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? 您每秒需要处理多少个UDP数据包? How many are being handled? 处理了多少? etc. 等等

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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