PyModbus 同步 vs 异步:不是选 API,而是选架构
你有没有遇到过这样的现场问题?
- 网关轮询 24 台电表,其中一台 Modbus TCP 设备突然断网,整个采集周期卡死 3 秒,连带 Web API 响应超时、告警延迟触发;
- 用
ThreadPoolExecutor(max_workers=50)拉起 50 个线程跑 Modbus,top一看 CPU 轻松破 80%,ps aux | grep python显示 50+sleeping线程挂在那里干等串口响应; - 写了个“每秒读一次寄存器”的脚本,在实验室跑得飞快,一上产线就丢数——因为真实设备响应时间波动在 10ms ~ 800ms 之间,而你的
timeout=1是按平均值拍的。
这些不是 Modbus 协议的问题,也不是设备质量问题。这是你在用同步思维调度异步世界里的 I/O。
而pymodbus自 v3.0.0 起提供的原生 asyncio 支持,恰恰是为这类场景量身打造的底层解法——它不只是一套新 API,而是一次对工业通信负载本质的重新建模。
先抛开文档,看一个真实对比
假设你要从三台设备(ID 分别为 1、2、3)各读 10 个保持寄存器,超时设为 1.5 秒:
| 行为 | 同步模式(ModbusTcpClient) | 异步模式(AsyncModbusTcpClient) |
|---|---|---|
| 耗时逻辑 | read(1)→ 等 120ms →read(2)→ 等 90ms →read(3)→ 等 650ms → 总耗时 ≈ 860ms | await read(1)、await read(2)、await read(3)同时发起→ 最慢那个决定总耗时 ≈ 650ms |
| 线程占用 | 占用 1 个 OS 线程全程阻塞 | 0 个额外线程,所有等待由事件循环统一托管 |
| 失败影响 | 若read(2)报错(如连接拒绝),read(3)不会执行 | read(2)失败不影响read(1)和read(3)的执行与返回 |
| 内存开销 | 每个线程栈默认 8MB(Linux),50 设备 ≈ 400MB 内存 | 协程栈仅 KB 级,200 并发协程内存增量 < 10MB |
这个差异不是“快一点”或“省点资源”,而是决定了你的程序能不能活过第一个网络抖动。
同步模式:简单,但代价藏在细节里
ModbusTcpClient看起来非常友好:实例化、调用、取结果、捕获异常——和 requests.get() 几乎一样直觉。但它的“友好”是有前提的:你只跟一台设备说话,且它永远在线、永远准时。
它到底在做什么?
当你写下这行代码:
result = client.read_holding_registers(address=0,