Внедрить «порог байтов» в ReadAsync - PullRequest
0 голосов
/ 12 марта 2019

Мне нужна помощь в чтении данных с последовательного порта, подключенного к устройству с интерфейсом RS-232. Я решил использовать стиль async / await, поскольку данные будут постоянно обновляться в графическом интерфейсе, который должен оставаться чувствительным к пользователю.

Приведенный ниже код является сущностью моей текущей реализации.

private async void MyReadFromSerialPortMethod()
{
    // (this.serialPort is a System.IO.Ports.SerialPort instance.)
    this.serialPort.DiscardInBuffer();
    this.serialPort.DiscardOutBuffer();

    this.serialPort.Write(/* bytes for "please send me some data now", 0, length */);

    byte[] header = new byte[3];
    await this.serialPort.BaseStream.ReadAsync(header, 0, header.Length);

    // do something with the header info first,
    // like check what kind of data is incoming

    byte[] data = new byte[5];
    await this.serialPort.BaseStream.ReadAsync(data, 0, data.Length);

    // do something with the data,
    // like show it to the user eventually
}

Моя проблема в том, что в приведенном выше коде вызовы ReadAsync обычно читают только один байт с входа и затем возвращаются из await (не всегда , но обычно). Если бы я использовал синхронные методы, я мог бы сделать так, чтобы ReceivedBytesThreshold содержал необходимое количество байтов, но не хотел бы удерживать поток пользовательского интерфейса, пока я делаю это. Насколько я знаю, для ReadAsync нет такого порога для задержки возвращения. Это было бы очень полезно.

Я реализовал следующее в качестве обходного пути, но мне это не кажется хорошим решением. Зацикливание на асинхронном методе похоже на то, что я просто пишу цикл ожидания занятости, чтобы дождаться заполнения входного буфера, и я мог бы также использовать синхронную версию. Хотя я знаю, что await, вероятно, в некоторой степени возвращает управление вызывающей стороне между каждым поступающим байтом, речь идет не о профилировании кода для скорости и эффективности, а скорее о том, каков правильный шаблон кодирования и использую ли я преимущество асинхронного программирования или препятствия.

    byte[] header = new byte[3];
    int bytesRead = 0;
    while (bytesRead < header.Length)
    {
        bytesRead += await this.serialPort.BaseStream.ReadAsync(header, bytesRead, header.Length-bytesRead);
    }
    // similarly for byte[] data...

1 Ответ

1 голос
/ 12 марта 2019

Чтобы получить многобайтовый ответ, вам все равно нужен цикл, поскольку свойство SerialPort.ReceivedBytesThreshold относится только к событию SerialPort.DataReceived.Но, вероятно, в сценарии «запрос-ответ» вам все равно нужно использовать синхронный API и Task.Run(), потому что асинхронный API полностью игнорирует свойства SerialPort timeout и CancellationToken почти полностью.Это означает, что асинхронные SerialPort операции могут зависать навсегда, удерживая буферы и, возможно, блокировки.

Также необходимо синхронизировать доступ к критическим секциям (логическим блокам взаимодействия с последовательным портом), чтобы множественные запросы не пересекались друг с другом (используя ключевое слово lock, экземпляр SemaphoreSlim или аналогичный).

Например, для реализации, пожалуйста, отметьте Событие C # await и время ожидания при обмене данными через последовательный порт обсуждение StackOverflow.

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

Для получения дополнительной информации проверьте:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...