Python多线程实现MQTT框架下不同速率IO采样的问题求助
多线程MQTT采样循环问题修复:高负载与任务不执行
你的代码里有几个致命问题,直接导致sampleTickPort持续运行、getJ1939Data完全不执行,还把系统负载拉满,同时时间计算逻辑全错:
核心问题拆解
- 线程池反复创建销毁:每次内层循环都新建
ThreadPoolExecutor,频繁创建销毁线程会耗尽系统资源,直接拉高负载 - 时间计算彻底错误:
- 变量拼写错误:
elaspedTimeBeforeTX和elapsedTimeBeforeTX混用,时间变量根本没正确更新 - 用
time.process_time()(CPU执行时间)累加,且仅在sampleTickPort执行后才更新,真实时间流逝完全没被统计,导致永远到不了3.5-4秒的触发区间 - 浮点数取模
elapsedTimeBeforeTX % .01 == 0存在精度坑,几乎不可能稳定触发10ms采样
- 变量拼写错误:
- 多线程形同虚设:
submit()后立刻调用result(),强制等待任务完成,完全变成串行执行,多线程等于没加 - 嵌套循环逻辑混乱:内层
while elapsedTimeBeforeTX <4.9会一直死循环,时间不推进的话永远跳不出去执行MQTT发送
修复后的代码
import time import concurrent.futures # 全局计数器(需要线程安全的话,可添加threading.Lock()) tickTotal = 0 j1939Data = [] tickPort = 1 # 替换为你的实际端口号 def sampleTickPort(port): # 替换为你的实际IO采样逻辑,返回每次采样的增量 return 1 def getJ1939data(): # 替换为你的实际数据获取解析逻辑,模拟1秒执行时间 time.sleep(1) return {"engine_param": "sample_value"} def buildMessage(tick_count, j1939_data): # 替换为你的MQTT消息构建逻辑 return {"tick_total": tick_count, "j1939": j1939_data} def sendMQTTMessage(payload): # 替换为你的MQTT消息发送逻辑 print(f"已发送MQTT消息: {payload}") def main(): # 全局只创建一次线程池,避免反复创建销毁线程 with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor: while True: cycle_start = time.time() elapsed = 0.0 j1939_task_done = False # 标记J1939任务是否已提交执行 while elapsed < 4.9: current_time = time.time() elapsed = current_time - cycle_start # 每10ms触发一次采样,用整数毫秒避免浮点数精度问题 if int(elapsed * 1000) % 10 == 0: # 提交采样任务,用回调更新计数器,不阻塞主线程 future = executor.submit(sampleTickPort, tickPort) def update_tick(fut): global tickTotal # 如需线程安全,此处加锁 # with tick_lock: tickTotal += fut.result() future.add_done_callback(update_tick) # 避免短时间内重复触发,休眠1ms time.sleep(0.001) # 在3.5-4秒区间内仅执行一次J1939数据获取 if 3.5 <= elapsed < 4.0 and not j1939_task_done: j1939_future = executor.submit(getJ1939data) j1939_task_done = True # 等待J1939任务完成后再构建消息 if j1939_task_done: j1939_data = j1939_future.result() j1939Data.append(j1939_data) # 构建并发送MQTT消息 payload = buildMessage(tickTotal, j1939Data) payload["timestamp"] = int(round(time.time() * 1000)) sendMQTTMessage(payload) # 确保整个循环周期为5秒,计算剩余休眠时间 cycle_elapsed = time.time() - cycle_start if cycle_elapsed < 5.0: time.sleep(5.0 - cycle_elapsed) if __name__ == "__main__": main()
关键修改点说明
- 复用线程池:全局只初始化一次线程池,彻底解决线程频繁创建销毁的负载问题
- 真实时间统计:用
time.time()计算从循环开始的真实流逝时间,确保时间推进完全符合预期 - 稳定触发采样:将时间转为整数毫秒后取模,避开浮点数精度坑,10ms采样能稳定触发
- 真正的多线程:采样任务用回调更新计数器,J1939任务在合适时机提交,主线程不阻塞,实现两个任务并行执行
- 保证5秒周期:每次循环结束后计算已用时间,休眠补足到5秒,确保MQTT上报频率稳定
内容的提问来源于stack exchange,提问作者WhiteRau
相关产品推荐
相关产品推荐

