Самый быстрый многопоточный метод анализа данных последовательного порта C # - PullRequest
7 голосов
/ 13 мая 2011

В настоящее время я пишу приложение, которое обменивается данными со встроенным сервоприводом через последовательное соединение.

Двигатель отправляет данные о положении со скоростью до 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));
                }
            }
        }
    }

Ответы [ 4 ]

3 голосов
/ 13 мая 2011

Ваш 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));
            }
        }
    }
}
2 голосов
/ 13 мая 2011

Есть несколько вещей, которые вы можете сделать, чтобы значительно улучшить это.

1) Создание анализатора конечного автомата, который анализирует поступающие данные по одному символу за раз. Когда оно построит полное «сообщение», добавьте его в List<MyMessage> структуру.

2) Используйте Virtualized ListView или DataGridView для отображения List<MyMessage>.

0 голосов
/ 13 мая 2011

В полученном событии прочитайте входящие данные как необработанные байты и сохраните их в очереди.Не обрабатывайте данные в обработчике событий.Затем используйте нечто похожее на то, что сказал альбин, для обработки данных другим методом.Важно разрешить обработчику событий запускать как можно чаще и не делать больше, чем требуется.

0 голосов
/ 13 мая 2011

В общем, я бы использовал блокирующую коллекцию между потоком чтения, который читает только из сокета, и потоком синтаксического анализа.
С точки зрения производительности, использование разделения для анализа - это намного быстрее, чем замена внутри строки,Вы должны рассмотреть использование регулярного выражения и / или класса StringBuilder - вы должны использовать 1 выражение с альтернативами
Код регулярного выражения будет выглядеть следующим образом:

string pattern = " |\\n|\\r";
string replacement = " "; 
regex rgx = new Regex(pattern);
string result = rgx.Replace(input, replacement);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...