如何用Python serial解决多线程读写串口的数据损坏问题?
多线程串口操作的线程安全处理方案
场景与问题
- 场景:一个线程通过USB串口每秒轮询温度传感器10次;用户按下按钮时,主线程通过同一串口修改传感器模式。
- 问题:
change_sensor_mode()的串口写入操作可能打断轮询线程的write/read_until操作,导致串口数据错乱。
原始示例代码
import threading, serial, time serial_port = serial.Serial(port="COM2", timeout=1.0) def poll_temperature(): while True: serial_port.write(b"GETTEMP\r\n") recv = serial_port.read_until(b"END") print(recv) time.sleep(0.2) def change_sensor_mode(): serial_port.write(b"CHANGEMODE2\r\n") t1 = threading.Thread(target=poll_temperature) t1.start() time.sleep(4.12342) # 模拟等待用户输入 change_sensor_mode()
手动锁尝试(非标准实现)
我曾尝试用全局变量手动控制锁状态,让主线程等待轮询操作完成:
global lock def change_sensor_mode(): elapsed = 0 t0 = time.time() while lock and elapsed < 1.0: time.sleep(0.010) elapsed += 0.010 serial_port.write(b"CHANGEMODE2\r\n")
标准解决方案:使用threading.Lock
Python标准库的threading.Lock是处理多线程资源竞争的专用工具,能保证同一时间只有一个线程访问串口资源,且自动管理锁的获取与释放,避免手动控制的疏漏。
优化后的代码
import threading, serial, time serial_port = serial.Serial(port="COM2", timeout=1.0) lock = threading.Lock() # 创建全局锁对象 def poll_temperature(): while True: # 使用with语句自动获取/释放锁,确保串口操作原子性 with lock: serial_port.write(b"GETTEMP\r\n") recv = serial_port.read_until(b"END") print(recv) time.sleep(0.2) def change_sensor_mode(): # 同样用with包裹串口写入操作 with lock: serial_port.write(b"CHANGEMODE2\r\n") t1 = threading.Thread(target=poll_temperature) t1.start() time.sleep(4.12342) # 模拟等待用户输入 change_sensor_mode()
带超时处理的进阶实现
如果需要在获取锁超时后给出提示,可以用acquire()方法手动控制锁:
def change_sensor_mode(): # 尝试1秒内获取锁,失败则放弃 if lock.acquire(timeout=1.0): try: serial_port.write(b"CHANGEMODE2\r\n") finally: lock.release() # 无论是否异常,确保释放锁 else: print("获取串口锁超时,无法修改传感器模式")
方案优势
- 自动管理锁生命周期,避免手动设置
lock变量时可能出现的死锁或锁未释放问题 - 保证串口的读写操作是原子性的,不会被其他线程打断
- 支持超时控制,灵活处理资源竞争场景
内容的提问来源于stack exchange,提问作者Basj
相关产品推荐
相关产品推荐

