PySerial多进程/客户端同时访问串口设备的机制与最佳实践
多进程访问USB-RS485电表设备的最佳实践
为什么多进程能同时打开串口?
不同操作系统对串口设备的独占性处理逻辑不一样:Linux默认允许多个进程打开同一个串口设备,而Windows通常会强制独占访问。你遇到的情况应该是在Linux类系统下,内核没阻止多进程打开,但这绝对不是安全操作——两个进程同时读写会直接导致数据混乱:比如A进程发的请求,B进程的readline()可能截走响应;或者两个进程的发送字节混在一起,电表根本无法解析命令。
频繁打开/关闭上下文管理器是否合理?
- 短期能用,但绝非最优:每次打开串口都会重置波特率、奇偶校验等参数,如果其他进程也在操作,很容易互相干扰参数设置。
- 额外开销:部分驱动下,频繁开关会触发USB设备重新枚举,增加延迟,次数多了还可能导致设备假死。
- 建议:除非必要,尽量保持一个长期有效的串口连接,用同步机制控制访问。
同时开启多个上下文(多进程打开串口)可行吗?
完全不可行,属于高危操作:
- 数据冲突:多进程同时发命令,电表收到的是混杂字节流,无法正确解析;返回的响应也会被多个进程争抢,结果全是错乱的。
- 硬件风险:部分RS485芯片在多进程同时驱动下,可能出现收发引脚状态冲突,长期这么搞可能损坏设备。
实现“随时可用的读数函数”的最优方案
核心逻辑是让单个进程/线程独占串口,其他请求通过同步机制排队访问,以下是两种落地方式:
方式1:单守护进程 + 进程间通信(适合多进程场景)
- 启动一个单独的守护进程,独占打开串口并保持连接。
- 其他需要读数的进程通过队列、管道或本地socket向守护进程发请求。
- 守护进程收到请求后,执行
get_values_str发命令、读响应,再把结果返回给请求方。
示例代码片段:
# 守护进程代码(serial_daemon.py) import serial from multiprocessing import Process, Queue def serial_worker(request_queue, response_queue): # 独占打开串口,长期保持连接 with serial.Serial('/dev/ttyUSB0', 9600, timeout=2) as ser: while True: cmd = request_queue.get() if cmd == 'GET_VALUES': ser.write(get_values_str) resp = ser.readline() response_queue.put(resp) elif cmd == 'EXIT': break if __name__ == '__main__': req_q = Queue() resp_q = Queue() daemon = Process(target=serial_worker, args=(req_q, resp_q)) daemon.start()
# 客户端进程调用代码 from multiprocessing import Queue import serial_daemon def get_meter_data(): serial_daemon.req_q.put('GET_VALUES') return serial_daemon.resp_q.get()
方式2:单进程内多线程 + 线程锁(适合单进程多任务场景)
如果所有读数逻辑都在同一个进程里,用线程锁控制串口访问:
- 全局维护一个串口对象和
threading.Lock实例。 - 每次调用读数函数时,先获取锁,再执行读写操作,完成后自动释放锁。
示例代码:
import serial import threading # 全局串口对象和锁 ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=2) serial_lock = threading.Lock() def get_meter_data(): # 用with自动管理锁的获取和释放 with serial_lock: ser.write(get_values_str) return ser.readline()
额外注意事项
- 串口参数必须统一:所有访问串口的代码要用完全相同的波特率、奇偶校验、停止位等参数,否则直接通信失败。
- 合理设置超时:读写操作时设置合适的超时时间,避免进程/线程因等待响应挂起。
- 异常捕获:添加串口断开、读写失败的异常处理,比如守护进程检测到断开时自动重连。
内容的提问来源于stack exchange,提问作者ljwobker
相关产品推荐
相关产品推荐

