C# 用于从udp套接字高速接收和处理数据的两个线程
在我的应用程序中,数据以高速率(450 Mbps-每个UDP数据包大小为1Kb)从UDP套接字接收。我需要两根线。第一个线程从套接字接收数据并将数据写入缓冲区(字节[]);第二个线程从缓冲区读取数据并进行处理。我需要在实时模式下接收和处理它们。 我使用变量“TableCounter”来保护两个线程,并对两个线程使用“SetThreadAffinityMask”,并将其设置为CPU的两个不同线程 我在C++中实现了这个SeReRIO,而且速度很快。但在c#中,我的应用程序很慢,我丢失了一些数据包(每个数据包中都有计数器,我会检查它)。 如何解决速度和数据丢失问题?哪种类型的缓冲区适合此senario(两个线程访问缓冲区)?请帮帮我C# 用于从udp套接字高速接收和处理数据的两个线程,c#,multithreading,sockets,C#,Multithreading,Sockets,在我的应用程序中,数据以高速率(450 Mbps-每个UDP数据包大小为1Kb)从UDP套接字接收。我需要两根线。第一个线程从套接字接收数据并将数据写入缓冲区(字节[]);第二个线程从缓冲区读取数据并进行处理。我需要在实时模式下接收和处理它们。 我使用变量“TableCounter”来保护两个线程,并对两个线程使用“SetThreadAffinityMask”,并将其设置为CPU的两个不同线程 我在C++中实现了这个SeReRIO,而且速度很快。但在c#中,我的应用程序很慢,我丢失了一些数据包(
/////////////////////////////////////
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
}
}
}
及
/////////////////////////////////////
第二线
/////////////////////////////////////
public void processData()
{
var ptr=GetCurrentThread();
SetThreadAffinityMask(ptr,新的IntPtr(0x0004));
int计数器=0;
waitHandle.WaitOne();
while(true)
{
如果((Counter
对接收代码的简要检查表明,您不需要“预分配”tdata。C#不仅分配内存,而且每次迭代都会将其归零。您为此付出了成本,然后将其丢弃,因为接收返回的是一个(不同的)缓冲区。请尝试以下操作:
var tdata = client.Receive(ref anyIP);
分配tdata和阵列。复制将是您花费时间的地方。A
“更多的C#ish”方法是将表声明为列表或数组,如下所示:
List<byte[]> Table = new List<byte[]>(10);
....
Table[ix] = client.Receive(ref anyIP);
ix++
if (ix > 10) ... do something cycle, etc.
List Table=新列表(10);
....
表[ix]=客户端接收(参考anyIP);
九++
如果(ix>10)…做某事,循环等。
这将完全删除tdata并将返回值分配给表中的相应索引。这当然需要将第二个线程函数更改为使用缓冲区列表结构。这些更改将两个1024字节的复制操作(一个用于初始化,一个用于数组.copy)减少为设置一个指针
您还需要小心,确保第一个(生产)线程不会超过第二个(消费)线程。我假设在生产中,第二个线程将执行更多操作,每次通过缓冲区1字节(第二个线程每次循环1024次)
回复评论的附加内容
评论中出现了两个问题。原来的问题似乎是实际问题的简化版本。但我建议采用相同的方法。“收获”/将紧密循环中的数据读取到预先分配的块数组中。例如,每个元素包含n字节的100个元素数组。返回的字节数由UdpClient.Recieve方法控制,并将等于发送的UDP数据包的数据部分。因为初始数组只是指针数组(指向字节[])您应该使其足够大,以处理适当数量的积压数据包。如果这是在内存量合理的PC上运行的,请从类似列表(16000)的内容开始,然后根据用户线程处理数据的速度向上或向下调整起始值
如果您认为UdpClientRecieve方法是性能瓶颈,请创建Diagnostics.Stopwatch对象并使用Stopwatch.Start/Stopwatch.Stop调用包装调用。跟踪这些调用以查看实际时间成本;例如:
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());
var tracker=List();
var stopwatch=new System.Diagnostics.stopwatch();
//做一些事情,然后输入while(true)循环
秒表复位();
接收(参考任何IP);
秒表;
Add(stopwatch.appeased.totalmillizes());
您可以在运行结束时将跟踪器的内容写入文件,或者使用调试器(可能还有linq)查看数据。这将准确地告诉您在接收过程中花费了多少时间,以及您是否遇到了任何来自GC的“暂停”
第二个问题似乎是你在你的消费线程(线程处理数据)中要做的工作量。如果你为每个数据块读了很多工作,那么你可能会考虑生成多个线程,每个线程在不同的块上工作。你可以有一个“调度器”。读取一个块并将其发送到工作线程池中的一个线程。Dispatcher块将监视表的增长,并在表变得太“大”时执行任何认为合适的操作
你可能会考虑把这个问题分解成多个问题,因为这样会更加紧密地关注问题和答案。首先,通过比较实际的时间和需要的或期望的时间来确定接收是否真的是问题。你需要处理多少个UDP数据包?多少个被处理?等等。ead.Sleep正在降低第二个线程的速度,执行一个线程。Sleep(0)或者更好的方法是,不要睡觉,当有新数据时,使用autoresetevent并从第一个线程发出信号。@Gusman,谢谢,但我的问题是在第一个线程中,一些时间很慢,数据包丢失了。你确定代码可以工作吗?我的意思是,你不检查你接收到多少数据,总是增加1024到TableCounter和n和1000比较,启动第二个线程,每次增加1024个线程时,不会发生任何事情……Gusman,对不起,我在这里键入代码有错误,正确的数字是10240,谢谢,我在C++中实现了这个代码,而且我没有任何丢失和工作得很好。这个代码在C中比C++慢得多,我检查了这个SEN。它没有任何丢失,但是这段代码不是实时的
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());