System.IO.Ports.SerialPort и многопоточность - PullRequest
3 голосов
/ 10 июня 2009

У меня есть некоторый код SerialPort, который постоянно должен считывать данные с последовательного интерфейса (например, COM1). Но, похоже, это сильно загружает процессор, и если пользователь перемещает окно или в него выводится много данных (например, байтов, полученных по последовательной линии), то связь нарушается.

Учитывая следующий код:

void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{

byte[] buffer = new byte[port.ReadBufferSize];

var count = 0;

try
{
    count = port.Read(buffer, 0, buffer.Length);
}
catch (Exception ex)
{
    Console.Write(ex.ToString());
}

if (count == 0)
    return;

//Pass the data to the IDataCollector, if response != null an entire frame has been received


var response = collector.Collect(buffer.GetSubByteArray(0, count));

if (response != null)
{
    this.OnDataReceived(response);
}

Код необходимо собирать, так как поток данных постоянен и данные должны быть проанализированы для (кадры / пакеты).

    port = new SerialPort();

    //Port configuration code here...

    this.collector = dataCollector;

    //Event handlers
    port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
    port.Open();

Если нет никакого взаимодействия с пользователем и ничего не добавляется в окно, это работает нормально, но как только взаимодействие становится действительно запутанным. Происходят тайм-ауты и т.д ....

Например, это все портит:

Dispatcher.BeginInvoke(new Action(() =>
{
  var builder = new StringBuilder();
  foreach (var r in data)
  {
      builder.AppendFormat("0x{0:X} ", r);
  }


  builder.Append("\n\n");

  txtHexDump.AppendText(builder.ToString());

  txtHexDump.ScrollToEnd();


}),System.Windows.Threading.DispatcherPriority.ContextIdle);
});

Но даже простые вызовы log4net вызывают проблемы.

Существуют ли передовые практики для оптимизации связи SerialPort? или кто-то может сказать мне, что я делаю не так ...

Обновление:

В случае, если вышеизложенное не имеет особого смысла. Я сделал очень простой (и глупый) маленький пример:

class Program
{
    static void Main(string[] args)
    {
        var server = new BackgroundWorker();
        server.DoWork += new DoWorkEventHandler(server_DoWork);
        server.RunWorkerAsync();

        var port = new SerialPort();
        port.PortName = "COM2";
        port.Open();
        string input = "";

        Console.WriteLine("Client on COM2: {0}", Thread.CurrentThread.ManagedThreadId);
        while (input != "/quit")
        {
            input = Console.ReadLine();
            if (input != "/quit")
            {
                var data = ASCIIEncoding.ASCII.GetBytes(input);
                port.Write(data, 0, data.Length);
            }
        }

        port.Close();
        port.Dispose();
    }

    static void server_DoWork(object sender, DoWorkEventArgs e)
    {
        Console.WriteLine("Listening on COM1: {0}", Thread.CurrentThread.ManagedThreadId);
        var port = new SerialPort();
        port.PortName = "COM1";
        port.Open();

        port.ReceivedBytesThreshold = 15;
        port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
    }

    static void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        var port = (SerialPort)sender;
        int count = 0;
        byte[] buffer = new byte[port.ReadBufferSize];
        count = ((SerialPort)sender).Read(buffer, 0, buffer.Length);

        string echo = ASCIIEncoding.ASCII.GetString(buffer,0,count);
        Console.WriteLine("-->{1} {0}", echo, Thread.CurrentThread.ManagedThreadId);
    }
}

Результат может выглядеть так:

Прослушивание на COM1: 6 Клиент на COM2: 10 Это пример данных, которые я отправляю ---> 6 Это пример данных, которые я отправляю

То есть чтение данных из порта происходит в главном потоке ....

Может ли это быть частью того, что вызывает мои проблемы?

Ответы [ 4 ]

13 голосов
/ 21 ноября 2012

Я удивлен, что никто не поймал это. Класс SerialPort использует свой собственный поток при использовании события DataReceived. Это означает, что если подписчик, например, имеет доступ к каким-либо элементам Form, то это должно быть сделано с помощью методов Invoke или BeginInvoke. В противном случае вы получите операцию с перекрестной резьбой. В старых версиях .net это могло бы остаться незамеченным с непредсказуемым поведением (в зависимости от ядер процессора в ПК), а в более поздних версиях должно возникать исключение.

4 голосов
/ 10 июня 2009

Ваш последний вывод о том, что событие выполняется в главном потоке, может быть неверным для приложения Windows. Не проверяйте это в консоли.

Правильный способ настроить это:

  • установить достаточно большой буфер, хотя обычно минимальное значение 4096 - ОК

  • установите значение ReceivedBytesThreshold как максимально допустимое (и сделайте это перед открытием ())

  • сделайте как можно меньше в полученном событии, передайте данные в очередь или MemoryStream, если вам нужно больше времени

2 голосов
/ 10 июня 2009

Вы должны переписать процедуру port_DataReceived, чтобы читать данные, пока port.BytesToRead не станет больше нуля, например:

private void port_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
        var port = (SerialPort)sender;
        while (port.BytesToRead > 0)
        {
            int byte_count = port.BytesToRead;
            byte[] buffer = new byte[byte_count];

            int read_count = port.Read(buffer, 0, byte_count);

            // PROCESS DATA HERE

        }
}

Также я бы порекомендовал вам просто вставить данные в список очереди в процедуре port_DataReceived и выполнить обработку данных в отдельном потоке.

1 голос
/ 10 июня 2009

Классическое решение - иметь буфер FIFO. Убедитесь, что размер FIFO достаточно велик для обработки любого критического случая, когда много ввода и занят блок процессора.

Вы могли бы даже иметь 2-буферную систему:

--->|Reader|-->FIFO-->|Processor|--->FIFO2--->|Displayer|
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...