Чтение PySerial () в потоке занимает много времени на Python 3 (.8.5)

#python #python-3.x #pyserial

#python #python-3.x #pyserial

Вопрос:

Я уже несколько лет с удовольствием использую PySerial (3.4) в сценариях Python на базе Windows (10) для работы со встроенными целями. В связи с неизбежной кончиной Python 2 я недавно перенес все свои скрипты Python 2.7.15 на Python 3, решив сразу перейти к Python 3.8.5. Однако я столкнулся с тем, что кажется проблемой с PySerial read() при запуске на Python 3.8.5.

Сценарий: у меня есть целевой HW, который передает потоковый вывод на COM-порт, который считывается скриптом со скоростью 115 200, без управления потоком. Я знаю, что цель работает, потому что я вижу, что она читается непрерывно, без задержек, с версией моего скрипта Python 2.7.15, и я также могу видеть это в PuTTY. Вывод состоит из строк текста ASCII, заканчивающихся символом «r n», которые передаются вместе. Однако в Python 3.8.5 (PySerial 3.4) для возврата с каждым символом read(1) требуется около 30 мс (измеряется путем сброса разницы между time() до и после read(1) вызова в список), так что получение 30 символов занимает около 1 секунды. Да, я знаю, что чтение одного символа неэффективно, но они определенно не настолько неэффективны; Я хотел бы контролировать поиск окончания строки, и, поскольку символ уже есть в буфере Windows, он должен быстро возвращаться (и делает это в Python 2).

Кто-нибудь еще видел проблему такого рода? Это, конечно, может быть как-то связано с поведением потоков в Python 3. Я мог бы попробовать другую версию Python 3, но у меня есть относительно большая структура скриптов Python для перемещения, поэтому это должно быть стоящей ставкой. Я поднял проблему в PySerial, но я боюсь, что она больше не может активно поддерживаться.

РЕДАКТИРОВАТЬ: если я удаляю свой read(1) из его потока, то он работает с той же скоростью, что и на Python 2.7, поэтому проблема не в PySerial, это как-то связано с тем, как изменилась обработка потоков в Python 3.

Подробно: я открываю последовательный порт следующим образом:

 in_handle = serial.Serial("COM8", 115200, timeout=0.05)
  

…и тогда у меня есть функция, запущенная в потоке, которая читается in_handle следующим образом:

         terminator = "r"
        line = ""
        eol = False
        return_value = None

        try:
            while not eol and line is not None:
                buf = in_handle.read(1)
                if buf:
                    character = buf.decode('ascii')
                    eol = character == terminator
                    if not eol:
                        line = line   character
                else:
                    line = None
            if eol:
                line = line.strip()
        except UnicodeDecodeError:
            # Just ignore it.
            pass
        return_value = line
  

Вывод из этого помещается в очередь:

 read_queue.put(return_value)
  

…и основной поток создает очередь / поток последовательного чтения и удаляет из нее содержимое с помощью:

     read_queue = queue.Queue()
    readline_thread = threading.Thread(target=readline_and_queue,
                                       args=(in_handle))
    readline_thread.start()

    while True:
        try:
            line = read_queue.get(block=False, timeout=0.5)
            # do something with the result
        except queue.Empty:
            pass
  

Когда я использую time() любую сторону read(1) вызова для измерения его продолжительности, значения, которые я получаю для строки длиной 34 символа, являются:

 0.03198051452636719
0.031983137130737305
0.031981468200683594
0.0159909725189209
0.031981706619262695
0.0319819450378418
0.0479733943939209
0.031982421875
0.0319826602935791
0.03198075294494629
0.031982421875
0.0319819450378418
0.0319826602935791
0.0159912109375
0.0159909725189209
0.03198122978210449
0.0319819450378418
0.0319821834564209
0.0319821834564209
0.0319821834564209
0.030982255935668945
0.03098273277282715
0.031981706619262695
0.0319826602935791
0.0319821834564209
0.031981706619262695
0.0319819450378418
0.0319819450378418
0.0319821834564209
0.0319828987121582
0.03198075294494629
0.031983375549316406
0.031981468200683594
0.0319821834564209 
  

Комментарии:

1. И оказывается, что я все еще могу запустить обновленный скрипт на Python 2, поэтому, если я изменю свой path, чтобы снова запустить Python27, он, конечно же, будет выполняться с ожидаемой скоростью (каждый read(1) занимает от 5 до 10 мс). Что происходит с Python 3 для меня?!

2. Исправлено! Моя собственная глупая ошибка: по причинам, которые я сейчас не помню, я сделал удаление из очереди неблокирующим, поэтому Python разрывал этот цикл, бессмысленно используя процессор. Вопрос в том, почему, черт возьми, это не было проблемой в Python 2?

Ответ №1:

Исправлено, моя собственная глупая ошибка: по причинам, которые я сейчас не помню, я сделал удаление из очереди неблокирующим, поэтому Python разрывал этот цикл, бессмысленно используя процессор. Вопрос в том, почему, черт возьми, это не было проблемой в Python 2?