Python PySerial非阻塞读取循环

Python PySerial非阻塞读取循环,python,python-3.x,nonblocking,pyserial,Python,Python 3.x,Nonblocking,Pyserial,我读取的串行数据如下: connected = False port = 'COM4' baud = 9600 ser = serial.Serial(port, baud, timeout=0) while not connected: #serin = ser.read() connected = True while True: print("test") reading = ser.readline().decode() 问

我读取的串行数据如下:

connected = False
port = 'COM4'
baud = 9600

ser = serial.Serial(port, baud, timeout=0)

while not connected:
    #serin = ser.read()
    connected = True

    while True:
        print("test")
        reading = ser.readline().decode()
问题是它阻止了其他任何东西的执行,包括web框架。添加
sleep()

将“while True”更改为“while ser.readline():”不会打印“test”,这很奇怪,因为它在Python2.7中工作。您知道有什么问题吗


理想情况下,我应该只能在串行数据可用时读取。数据每1000毫秒发送一次。

将其放在单独的线程中,例如:

导入线程
导入序列号
已连接=错误
端口='COM4'
波特率=9600
串行端口=串行。串行(端口,波特率,超时=0)
def句柄_数据(数据):
打印(数据)
def从端口(ser)读取:
未连接时:
#serin=ser.read()
已连接=真
尽管如此:
打印(“测试”)
reading=ser.readline().decode()
处理数据(读取)
thread=threading.thread(目标=从\u端口读取,\u,args=(串行\u端口,))
thread.start()

完全不需要使用单独的线程。只需对无限while循环执行此操作即可(在Python 3.2.3中测试)。我在串行终端程序中使用此技术

通过这种方式,您只能在存在某些内容的情况下读取和打印。您说,“理想情况下,我应该只能在串行数据可用的情况下读取串行数据。”这正是上面的代码所做的。如果没有可读取的内容,它将跳过while循环中的其余代码。完全无阻塞

(此答案最初在此处发布和调试:)

pySerial文档:

更新:

  • 2018年12月27日:添加了关于
    in_waiting
    vs
    in waiting()
    的评论。感谢@FurkanTürkal在下面的评论中指出了这一点。请参阅此处的文档:
  • 2018年10月27日:添加睡眠让其他线程运行
  • 文件:
  • 感谢@RufusV2在评论中提出这一点
关于多线程的注意事项:
尽管如上所示,读取串行数据不需要使用多个线程,但以非阻塞方式读取键盘输入也需要。因此,为了完成非阻塞键盘输入读取,我写了以下回答:。

使用计时器驱动的事件来测试和读取串行端口。 未经测试的示例:

import threading
class serialreading():
    def __init__(self):
        self.active = True
        self.test()

    def test(self):
        n_in =comport.in_waiting()
        if n_in> 0:
            self.data = self.data + comport.read(size=n_in)
        if len(self.data) > 0: 
             print(self.data)
             self.data=""
        if self.active:
             threading.Timer(1, test).start()  # start new timer of 1 second

    def stop(self):
        self.active = False

我要警告不要在线程中使用阻塞IO。记住Python有一个线程,并且一次只能执行一个线程。现在请注意,pyserial模块是访问串行端口的OS实现的包装器。这意味着它调用Python外部的代码。如果代码阻塞,那么解释器也会被阻塞,而不会执行任何操作将在Python程序中执行,甚至是在主线程中

如果底层设备驱动程序没有很好地实现超时,甚至在使用非阻塞IO或基于超时的轮询时也会发生这种情况


一种更可靠的方法是使用模块。在单独的进程中运行串行读取代码。这将确保主线程和其他线程不会阻塞,程序可以干净地退出。

您不创建一个线程并将此读取代码添加到其中吗?串行通信正在阻塞…您应该使用线程您可以在考试中发布答案吗ple?Serial正在阻塞…除非您将其设置为非阻塞。(除非他们在py 2.4之后更改了某些内容)我刚刚在下面发布了我的答案。此外,我建议您在这个问题中添加关键字“非阻塞”。谢谢!这个解决方案让我今天摆脱了困境。我真的觉得在这种情况下,这应该是可以接受的答案。而不是while(True)我建议对PySerial Version>3使用while(ser.isOpen())您需要使用ser.is_open如果您没有阻塞,我建议在if..inWaiting块中使用一个else子句,带有time.sleep(0.01)以避免“挂起”“如果您希望同时运行其他任何操作,请打开计算机的CPU。在版本3.0中更改:将属性从
inWaiting()
更改为
in_waiting()
请。也请参阅队列使用的通用多线程示例,请参阅此处:。或者只需使用pyserial asyncio并在回调上执行某些操作。使用qt5计时器对我来说是一个很好的选择。它在GUI应用程序中提供其他IO事件处理(键盘、鼠标)和结果表示。它避免了线程的使用/导入。GIL仅保存在解释器中。在你的链接中,“注意潜在的阻塞或长时间运行的操作,如I/O、图像处理和NumPy数字处理,发生在GIL之外。”重点是避免等待外部发生的事情。通过使用非阻塞I/O,您的程序可以自由地做下一件它想做的有用的事情。
import threading
class serialreading():
    def __init__(self):
        self.active = True
        self.test()

    def test(self):
        n_in =comport.in_waiting()
        if n_in> 0:
            self.data = self.data + comport.read(size=n_in)
        if len(self.data) > 0: 
             print(self.data)
             self.data=""
        if self.active:
             threading.Timer(1, test).start()  # start new timer of 1 second

    def stop(self):
        self.active = False