开发者

Fastest Multi-Threading Method of Serial Port Data Parsing C#

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.

Cu开发者_JS百科rrently, 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));
                }
            }
        }
    }


Your parseBuffer will go wild spinning even if there is no new data since last try.

You can mitigate this with signalling.

private AutoResetEvent waitHandle = new AutoResetEvent(false);

Trigger the signal in 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
       }

}

wait for the signal in 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));
            }
        }
    }
}


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>.


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.


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:

string pattern = " |\\n|\\r";
string replacement = " "; 
regex rgx = new Regex(pattern);
string result = rgx.Replace(input, replacement);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜