C# 串口数据解析的最快多线程方法#
我目前正在编写一个应用程序,通过串行连接与集成伺服进行通信 电机以高达1000次/秒的速率发送位置数据。我试图实现的是能够格式化返回的数据(通过去除空白、新行等),并对其进行解析以从接收到的字符串中提取相关数据 目前,我让data received事件处理程序读取数据,使用一系列string.replace方法调用对其进行格式化,并将其附加到充当缓冲区的字符串中。然后,使用线程,在缓冲区填充特定分隔符(在我的示例中为“\r”),表示来自马达的一条消息的结束时,我不断检查缓冲区,然后从缓冲区中删除该消息并将其打印到富文本字段 这种方法有两个问题。一个原因是,由于马达以如此高的速率传输位置数据,因此缓冲区的填充速度比线程处理数据的速度快。因此,当我向马达发送命令时,它会立即动作,但响应会延迟几秒钟,因为必须首先处理缓冲区中所有前面的数据。其次,让两个线程运行一个实现while(true)结构的方法意味着处理器利用率猛增,几秒钟内pc中的风扇达到最大 是否有更好的方法处理数据 以下是我的事件处理程序代码:C# 串口数据解析的最快多线程方法#,c#,parsing,serial-port,C#,Parsing,Serial Port,我目前正在编写一个应用程序,通过串行连接与集成伺服进行通信 电机以高达1000次/秒的速率发送位置数据。我试图实现的是能够格式化返回的数据(通过去除空白、新行等),并对其进行解析以从接收到的字符串中提取相关数据 目前,我让data received事件处理程序读取数据,使用一系列string.replace方法调用对其进行格式化,并将其附加到充当缓冲区的字符串中。然后,使用线程,在缓冲区填充特定分隔符(在我的示例中为“\r”),表示来自马达的一条消息的结束时,我不断检查缓冲区,然后从缓冲区中删除
//data recieved event handler
private void dataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string tmp;
tmp = sp.ReadExisting();
//cut out any unnecessary characters
tmp = tmp.Replace("\n", "");
tmp = tmp.Replace(",", "\r");
tmp = tmp.Replace(" ", "");
lock (this)
{
//put all received data into the read buffer
readBuffer += tmp;
}
}
以下是线程执行的方法:
private void parseBuffer()
{
while (true)
{
//obtain lock, parse one message from buffer
lock (this)
{
if (readBuffer.IndexOf("\r") > 0)
{
String t = readBuffer.Substring(0, readBuffer.IndexOf("\r") + 1);
readBuffer = readBuffer.Replace(t, "");
dataReady(this, new CustomEventArgs(t, null));
}
}
}
}
一般来说,我会在只从套接字读取的读取器线程和解析线程之间使用阻塞集合。
在性能方面,请看使用拆分进行解析——这比在字符串中替换要快得多。您应该考虑使用正则表达式和/或StringBuilder类-您应该使用1个表达式和可选项
正则表达式代码如下所示:
string pattern = " |\\n|\\r";
string replacement = " ";
regex rgx = new Regex(pattern);
string result = rgx.Replace(input, replacement);
你可以做一些事情来显著改善这一点 1) 构建一个状态机解析器,该解析器一次解析一个字符的输入数据。当它构建了一个完整的“消息”后,将其添加到
列表
结构中
2) 使用虚拟化的ListView或DataGridView显示
列表
即使自上次尝试后没有新数据,您的解析缓冲区
也会疯狂旋转
您可以通过发送信号来缓解这种情况
private AutoResetEvent waitHandle = new AutoResetEvent(false);
在接收到的数据中触发信号
//data recieved event handler
private void dataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string tmp;
tmp = sp.ReadExisting();
//cut out any unnecessary characters
tmp = tmp.Replace("\n", "");
tmp = tmp.Replace(",", "\r");
tmp = tmp.Replace(" ", "");
lock (this)
{
//put all received data into the read buffer
readBuffer += tmp;
waitHandle.Set(); // <-- tell parseBuffer that new data is available
}
}
在DataReceived事件中,将传入数据作为原始字节读取并存储在队列中。不要处理事件处理程序中的数据。然后使用类似于阿尔宾所说的方法,用另一种方法处理数据。重要的是允许eventhandler尽可能频繁地触发,并且不再执行所需的操作。但如果我的目标是删除字符,则需要同时使用split命令和concatenation命令。这会更快?为什么要先删除字符?这是一个非常昂贵的操作,只有在内存出现问题时才执行。如果不是因为几个消息可能一次放在缓冲区上,那么这会起作用,因此通过设置waitHandle,只处理一条消息。我将“if(readBuffer.IndexOf(“\r”)>0)”更改为while(readBuffer.IndexOf(“\r”)>0)”每个guidilines都应该避免。这有点像TCP流,你需要确保检查你接收到的信息是否可能超过你的预期,因为有时一次接收操作可以接收多个发送。我发现了问题。不是程序不能足够快地处理缓冲区中的数据,而是程序cant以足够快的速度写入富文本框。我使用的代码只是执行了类似于
textBox1.text+=myText;
的操作。当打印到控制台时,我没有遇到缓冲区增长速度快于程序处理上述数据速度的问题。
private void parseBuffer()
{
while (true)
{
waitHandle.WaitOne(); // <-- waits until there is more data to parse
//obtain lock, parse one message from buffer
lock (this)
{
if (readBuffer.IndexOf("\r") > 0)
{
String t = readBuffer.Substring(0, readBuffer.IndexOf("\r") + 1);
readBuffer = readBuffer.Replace(t, "");
dataReady(this, new CustomEventArgs(t, null));
}
}
}
}