最快的串口数据解析多线程方法C#

时间:2011-05-13 14:47:10

标签: c# parsing serial-port

我目前正在编写一个通过串行连接与集成伺服器通信的应用程序。

电机以高达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));
                }
            }
        }
    }

4 个答案:

答案 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所说的用另一种方法处理数据的东西。重要的是允许事件处理程序尽可能频繁地触发,并且不再需要它。