我目前正在编写一个通过串行连接与集成伺服器通信的应用程序。
电机以高达1000次/秒的速率发送位置数据。我想要实现的是能够格式化回来的数据(通过剥离空格,新行等)并解析它以从接收的字符串中提取相关数据。
目前,我有数据接收事件处理程序读取数据,使用一系列string.replace方法调用对其进行格式化,并将其附加到充当缓冲区的字符串。然后,使用线程,我不断检查缓冲区,因为它填充特定的分隔符(在我的情况下为“\ r”),表示来自电机的一条消息的结束,然后从缓冲区中删除该消息并将其打印到富有文本字段。
这种方法存在两个问题。一个是因为电机以如此高的速率传输位置数据,所以缓冲器填充的速度比线程可以处理的数据快。因此,当我向电机发送命令时,它会立即动作,但响应会延迟几秒钟,因为必须首先处理缓冲区中的所有前面的数据。其次,让两个线程运行一个实现while(true)结构的方法意味着处理器利用率急剧上升,并且在几秒钟内,pc中的风扇最大化。
有更好的方法来处理数据吗?
这是我的事件处理程序代码:
//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));
}
}
}
}
答案 0 :(得分:3)
即使自上次尝试后没有新数据,您的parseBuffer
也会疯狂旋转。
您可以通过信号缓解这一点。
private AutoResetEvent waitHandle = new AutoResetEvent(false);
触发dataReceived
//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
}
}
等待parseBuffer
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));
}
}
}
}
答案 1 :(得分:2)
您可以采取一些措施来大幅改善这一点。
1)构建一个状态机解析器,一次解析一个字符的输入数据。当它构建完整的“消息”时,将其添加到List<MyMessage>
结构。
2)使用Virtualized ListView或DataGridView显示List<MyMessage>
。
答案 2 :(得分:0)
一般情况下,我会在只读取套接字的读取器线程和解析线程之间使用阻塞集合
在性能方面看看使用split进行解析 - 这比替换字符串要快得多。您应该查看使用正则表达式和/或StringBuilder类 - 您应该使用1表达式和备选方案
正则表达式代码如下所示:
string pattern = " |\\n|\\r";
string replacement = " ";
regex rgx = new Regex(pattern);
string result = rgx.Replace(input, replacement);
答案 3 :(得分:0)
在接收的数据事件中,将输入数据作为原始字节读取并存储在队列中。不要处理事件处理程序中的数据。然后使用类似于albin所说的用另一种方法处理数据的东西。重要的是允许事件处理程序尽可能频繁地触发,并且不再需要它。