C# 如何改进这个性能不佳、糟糕的串行端口代码?
我有一段很难看的串口代码,非常不稳定C# 如何改进这个性能不佳、糟糕的串行端口代码?,c#,.net,performance,serial-port,C#,.net,Performance,Serial Port,我有一段很难看的串口代码,非常不稳定 void port_DataReceived(object sender, SerialDataReceivedEventArgs e) { Thread.Sleep(100); while (port.BytesToRead > 0) { var count = port.BytesToRead; byte[] buffer = new byte[count]; var re
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(100);
while (port.BytesToRead > 0)
{
var count = port.BytesToRead;
byte[] buffer = new byte[count];
var read = port.Read(buffer, 0, count);
if (DataEncapsulator != null)
buffer = DataEncapsulator.UnWrap(buffer);
var response = dataCollector.Collect(buffer);
if (response != null)
{
this.OnDataReceived(response);
}
Thread.Sleep(100);
}
}
如果我删除其中一个Thread.Sleep(100)调用,代码将停止工作
当然,这确实会减慢速度,如果大量数据流进入,
除非我睡得更香,否则它也会停止工作。
(在纯死锁中停止工作)
请注意,数据封装器和数据收集器是组件
由MEF提供,但性能相当好
该类有一个Listen()方法,该方法启动后台工作程序
接收数据
public void Listen(IDataCollector dataCollector)
{
this.dataCollector = dataCollector;
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
port = new SerialPort();
//Event handlers
port.ReceivedBytesThreshold = 15;
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
..... remainder of code ...
欢迎提出建议
更新:
*只是简单介绍一下IDataCollector类的功能。
无法知道是否已发送所有字节的数据
在一次读取操作中读取。所以每次读取数据时,它都是
传递给DataCollector,当完成
已收到有效的协议消息。在这种情况下,它只是
检查同步字节、长度、crc和尾部字节。真正的工作
以后由其他类完成。
*
更新2:
我现在按照建议替换了代码,但仍然存在一些问题:
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var count = port.BytesToRead;
byte[] buffer = new byte[count];
var read = port.Read(buffer, 0, count);
if (DataEncapsulator != null)
buffer = DataEncapsulator.UnWrap(buffer);
var response = dataCollector.Collect(buffer);
if (response != null)
{
this.OnDataReceived(response);
}
}
您可以看到,这在快速稳定的连接下运行良好。
但并非每次接收数据时都调用OnDataReceived。
(有关更多信息,请参阅MSDN文档)。因此,如果数据变得支离破碎
在事件数据丢失时,只读取一次
现在我想起了为什么我一开始就有这个循环,因为
如果连接速度慢或不稳定,它实际上必须读取多次
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(100);
while (port.BytesToRead > 0)
{
var count = port.BytesToRead;
byte[] buffer = new byte[count];
var read = port.Read(buffer, 0, count);
if (DataEncapsulator != null)
buffer = DataEncapsulator.UnWrap(buffer);
var response = dataCollector.Collect(buffer);
if (response != null)
{
this.OnDataReceived(response);
}
Thread.Sleep(100);
}
}
显然,我不能回到while循环解决方案,那么我能做什么呢?而且它可以非常简单 要么使用DataReceived处理程序,但没有循环,当然也没有Sleep(),读取准备好的数据并将其推送到某个地方(队列或内存流) 或 启动一个线程(BgWorker)并执行(阻塞)serialPort1.Read(…),然后再次推送或组装您获得的数据 编辑: 根据您发布的内容,我会说:删除eventhandler并读取Dowork()中的字节。这样做的好处是,您可以指定所需的数据量,只要它比ReadBufferSize小(很多) Edit2,关于更新2: 在BgWorker中使用while循环仍然会更好,根本不使用事件。简单的方法是:
byte[] buffer = new byte[128]; // 128 = (average) size of a record
while(port.IsOpen && ! worker.CancelationPending)
{
int count = port.Read(buffer, 0, 128);
// proccess count bytes
}
现在,可能您的记录大小可变,您不想等待接下来的126个字节来完成一个。您可以通过减小缓冲区大小或设置读取超时来对此进行调整。要获得非常精细的粒度,可以使用port.ReadByte()。由于它从ReadBuffer中读取数据,所以速度并没有变慢。我对原始的基于while的代码片段的第一个关注点是字节缓冲区的恒定内存分配。在这里放置一个“new”语句,专门用于.NET内存管理器,为缓冲区分配内存,同时获取在上一次迭代中分配的内存,并将其发送回未使用的池,以便最终进行垃圾收集。在一个相对紧密的循环中,这似乎需要做很多工作 我很好奇,如果在设计时以合理的大小(比如8K)创建此缓冲区,您将获得性能改进,这样您就不会有所有这些内存分配、释放和碎片。那会有帮助吗
private byte[] buffer = new byte[8192];
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(100);
while (port.BytesToRead > 0)
{
var count = port.BytesToRead;
var read = port.Read(buffer, 0, count);
// ... more code
}
}
在循环的每次迭代中重新分配这个缓冲区的另一个问题是,如果缓冲区已经足够大,那么重新分配可能是不必要的。考虑以下事项:
- 循环迭代1:接收100字节;分配100字节的缓冲区
- 循环迭代2:接收75字节;分配75字节的缓冲区
另一方面,我可能建议DataReceived循环只关注数据的接收。我不确定这些MEF组件在做什么,但我怀疑它们的工作是否必须在数据接收回路中完成。是否可以将接收到的数据放在某种队列中,MEF组件可以在那里拾取它们?我对尽可能快地保持DataReceived循环感兴趣。也许可以将接收到的数据放在队列中,以便它可以直接返回到接收更多数据的工作中。您可能可以设置另一个线程来监视队列中的数据,并让MEF组件从队列中提取数据并从队列中执行工作。这可能需要更多的编码,但可能有助于数据接收环路尽可能响应。如果要将数据写入文件,并且串行端口经常停止,这是一种简单的方法。如果可能,请将缓冲区设置为足够大,以容纳计划放入单个文件中的所有字节。然后在datareceived事件处理程序中编写代码,如下所示。然后,当您有机会将整个缓冲区写入一个文件时,如下所示。如果必须在串行端口读取缓冲区时读取缓冲区,请尝试使用缓冲流对象以避免死锁和争用情况
private byte[] buffer = new byte[8192];
var index = 0;
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
index += port.Read(buffer, index, port.BytesToRead);
}
void WriteDataToFile()
{
binaryWriter.Write(buffer, 0, index);
index = 0;
}
嗨,这就是DataCollector的工作。DataCollector收集所有字节,直到有完整的协议消息为止。每次调用collect时,它都将返回false,直到有完整的协议消息为止。我会在原来的问题中加入这一点。我必须马上研究你的建议我已经在编辑了。我建议在BgWorker中进行阻塞读取。我认为您已经解决了我的问题:)我将进行更多测试,但目前您的答案是正确的:DDoS不适用于慢速c