C#最快的多线程串口数据解析方法
我目前正在编写一个通过串行连接与集成伺服系统通信的应用程序。
电机以高达1000次/秒的速率发出位置数据。我想要实现的是能够格式化返回的数据(通过去除空格、换行符等)并解析它以从接收到的字符串中提取相关数据。
目前,我让数据接收事件处理程序读取数据,使用一系列 string.replace 方法调用对其进行格式化,并将其附加到充当缓冲区的字符串中。然后,使用线程,我不断地检查缓冲区,因为它填充了特定的分隔符(在我的例子中为“\r”),这表示来自电机的一条消息的结束,然后从缓冲区中删除该消息并将其打印到丰富的文件中。文本字段。
这种方法有两个问题。一是因为电机以如此高的速率传输位置数据,所以缓冲区填充的速度比线程处理数据的速度快。因此,当我向电机发送命令时,它会立即动作,但响应会延迟几秒钟,因为必须首先处理缓冲区中的所有先前数据。其次,有两个线程运行实现 while(true) 结构的方法意味着处理器利用率飙升,并且在几秒钟内电脑中的风扇就会达到最大。
有没有更好的方法来处理数据?
这是我的事件处理程序代码:
//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));
}
}
}
}
I am currently writing an application that communicates with an integrated servo via a serial connection.
The motor sends out position data at a rate of up to 1000 times/second. What I'm trying to achieve is to be able to format the data coming back (by stripping it of white spaces, new lines, etc) and parsing it to extract the relevant data from the received strings.
Currently, I have the data received event handler read the data, format it using a series of string.replace method calls, and append it to a string that acts as a buffer. Then, using threads, I constantly check the buffer as it fills for a particular delimiter (in my case "\r") which signifies the end of one message from the motor, then remove that message from the buffer and print it to a rich text field.
There are two problems with this approach. One is that because the motor streams position data at such a high rate, the buffer fills faster than the data can be processed by the threads. Thus when I send a command to the motor, it acts immediately but the response is delayed by a few seconds because all of the preceding data in the buffer must be processed first. Second, having two threads running a method that implements a while(true) structure means processor utilization skyrockets and within a few seconds the fans in the pc are on max.
Is there a better way of handling the data?
Here is my event handler code:
//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;
}
}
Here is the method that the threads execute:
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));
}
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
即使自上次尝试以来没有新数据,您的
parseBuffer
也会疯狂旋转。您可以通过信令来缓解这种情况。
触发
dataReceived
中的信号等待
parseBuffer
中的信号Your
parseBuffer
will go wild spinning even if there is no new data since last try.You can mitigate this with signalling.
Trigger the signal in
dataReceived
wait for the signal in
parseBuffer
您可以采取一些措施来显着改善这一点。
1) 构建一个状态机解析器,一次解析一个字符传入的数据。当它构建了完整的“消息”后,将其添加到
List
结构中。2) 使用虚拟化ListView或DataGridView显示
List
。There are a couple of things you can do to improve this dramatically.
1) Build a state-machine parser that parses the incomming data one character at a time. When it has built a complete "message", add it to a
List<MyMessage>
structure.2) Use a Virtualized ListView or DataGridView to display the
List<MyMessage>
.在数据接收事件中,将传入数据作为原始字节读取并存储在队列中。不要在事件处理程序中处理数据。然后使用类似于albin所说的东西以另一种方法处理数据。重要的是让事件处理程序尽可能频繁地触发并且不再执行所需的操作。
In the data received event read the incoming data as raw bytes and store in a queue. Don't process the data in the event handler. Then use something similar to what albin said to process the data in another method. The important thing is to allow the eventhandler to fire as often as possible and do no more that required.
一般来说,我会在仅从套接字读取的读取器线程和解析线程之间使用阻塞集合。
在性能方面,考虑使用 split 进行解析 - 这比在字符串内替换要快得多。您应该考虑使用正则表达式和/或 StringBuilder 类 - 您应该使用 1 个表达式和替代方案
正则表达式代码如下所示:
In general I would use a blocking collection between a reader thread which solely reads from the socket and a parsing thread.
In terms of performance look at using split for parsing - this is a lot faster than replacing inside a string. You should look at using a regular expression and/or the StringBuilder class - you should use 1 expression with alternatives
The regex code would look like this: