Pyserial使用in_waiting致CPU占用过高的低负载串口监听方案咨询
解决方案
你当前CPU占用过高的核心原因是循环无阻塞轮询in_waiting属性,无数据时会以最高频率反复调用该接口,导致空转占用CPU。
方案1:轮询加短休眠(最低改造成本)
如果对报文延迟要求不高,直接在循环末尾添加毫秒级休眠即可,可将CPU占用降到接近0:
import time def listen(self): """ Main listener """ while self.doListen: if self.bus.ser.in_waiting: msg = self.bus.read(self.bus.ser.in_waiting) self.msgQueue.put_nowait(msg) # 1ms休眠可根据实时性要求调整,常规串口波特率下不会丢包 time.sleep(0.001)
方案2:阻塞读首字节+批量读剩余(推荐,CPU占用最低)
如果设置timeout=None后read(1)仍然返回空,优先排查串口配置是否生效、是否有其他线程同时操作串口、串口驱动是否存在兼容性问题,这不属于pyserial的常规表现。确认read(1)可正常阻塞后,可以用阻塞等首字节的方式,无数据时线程完全休眠不占用CPU,同时适配可变长度无分隔符的报文场景:
import time def listen(self): """ Main listener """ while self.doListen: # 无数据时阻塞在此,不占用CPU first_byte = self.bus.ser.read(1) if not first_byte: time.sleep(0.01) continue # 读取当前缓冲区所有剩余数据 remaining = self.bus.ser.read(self.bus.ser.in_waiting) msg = first_byte + remaining self.msgQueue.put_nowait(msg)
方案3:IO多路复用(适用于Linux/macOS系统)
类Unix系统下可以用select监听串口文件描述符的可读事件,无数据时线程阻塞在select调用上,CPU占用极低:
import select def listen(self): """ Main listener """ while self.doListen: # 超时设为1秒,可响应doListen的终止信号 readable, _, _ = select.select([self.bus.ser.fileno()], [], [], 1) if readable: msg = self.bus.read(self.bus.ser.in_waiting) self.msgQueue.put_nowait(msg)
注:Windows系统下pyserial的串口句柄不支持
select调用,该方案无法使用。
额外建议
如果你的串口波特率超过1Mbps、对实时性要求极高,建议使用C扩展或者更底层的串口库处理,Python层的调度延迟可能会影响数据接收稳定性。
内容的提问来源于stack exchange,提问作者Axel
相关产品推荐
相关产品推荐

