You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从外部Python模块调用函数耗时15秒,求助排查解决

COM端口扫描模块化后性能骤降的排查与解决

问题描述

为实现代码模块化,将COM端口扫描、选择函数迁移至独立模块Snippets\COMSelector.py,替代原主程序内的代码复制逻辑。主程序内直接定义这些函数时,执行瞬间完成;但通过from Snippets import COMSelector调用COMScanner()和targetSelector()时,单次执行耗时长达15秒(测试仅连接1个COM接口,排除人工操作影响)。

核心代码

COMSelector.py模块代码

from serial.tools import list_ports
from serial import Serial, SerialException
import tkinter as tk
from functools import partial


def COMScanner():
    allports = []
    ports = list_ports.comports()
    for port, desc, hwid in sorted(ports):
        hwid = hwid.split(" SER")[0]
        # print("{}: {} [{}]".format(port, desc, hwid)) #DBG
        try:
            s = Serial(port)
            s.close()
            state = "FREE"
        except (OSError, SerialException):
            state = "BUSY"
            pass

        allports.append([port, hwid, state])

    return allports


def targetSelector(COMS):
    global sel_com
    sel_com = None
    # 检测到多个COM端口时,用户需选择目标端口
    if COMS and len(COMS) > 1:
        COMSelector = tk.Tk()
        baseWidth = 8 * len("".join(max(COMS, key=len)))
        baseHeight = 35 * len(COMS)
        finalWidth = baseWidth + 10
        finalHeight = baseHeight  # * len(COMS)
        screen_width = COMSelector.winfo_screenwidth()
        screen_height = COMSelector.winfo_screenheight()
        x_coordinate = int((screen_width / 2) - (finalWidth / 2))
        y_coordinate = int((screen_height / 2) - (finalHeight / 2))

        COMSelector.geometry("{}x{}+{}+{}".format(finalWidth, finalHeight, x_coordinate, y_coordinate))
        COMSelector.update_idletasks()
        COMSelector.title("Select Target")

        def save_action(selection):
            global sel_com, user_pass
            sel_com = str(selection)
            COMSelector.destroy()

        for id, COM in enumerate(COMS):
            COM_HANDLE = str(COM[0])
            COM_PID = str(COM[1])
            COM_STATE = str(COM[2])
            action = partial(save_action, COM_HANDLE)
            COMLabel = tk.Label(master=COMSelector, text=COM_PID, borderwidth=0.5, relief="solid")
            COMButton = tk.Button(master=COMSelector,
                                  text=("[" + COM_STATE + "] " + COM_HANDLE),
                                  command=action
                                  )
            if COM_STATE == "FREE":
                COMButton.config(state="active")
            else:
                COMButton.config(state="disabled")

            COMButton.grid(row=id, column=0, pady=5, padx=5, sticky="WE")
            COMLabel.grid(row=id, column=1, pady=5, padx=5, sticky="WE")
        COMSelector.mainloop()

    else:
        sel_com = COMS[0]
    try:
        return sel_com
    except:
        return "No target selected."

主程序调用代码

from Snippets import COMSelector
from datetime import datetime  # 补全缺失的导入

[...]
start_time = datetime.now()
available_COMS = COMSelector.COMScanner()
target = COMSelector.targetSelector(available_COMS)
print(datetime.now() - start_time)

问题排查与解决方案

1. 解决Serial连接无超时阻塞问题

COMScanner中创建Serial(port)时未指定超时参数,默认是无限等待,若遇到未响应的端口会导致长时间阻塞。这是最可能的性能瓶颈。

优化代码:
给Serial实例添加超时限制,比如timeout=1:

try:
    s = Serial(port, timeout=1)  # 限制连接尝试时间为1秒
    s.close()
    state = "FREE"
except (OSError, SerialException):
    state = "BUSY"

2. 优化端口枚举速度

list_ports.comports()默认会枚举所有类型的端口,包括虚拟端口,可能导致枚举延迟。可以排除虚拟端口加快速度:

ports = list_ports.comports(include_links=False)

3. 移除全局变量避免上下文冲突

targetSelector中使用的全局变量sel_com在模块上下文可能存在意外状态,改用nonlocal替代全局变量:

def targetSelector(COMS):
    sel_com = None
    if COMS and len(COMS) > 1:
        COMSelector = tk.Tk()
        # ... 界面代码保持不变 ...
        
        def save_action(selection):
            nonlocal sel_com
            sel_com = str(selection)
            COMSelector.destroy()
        
        # ... 界面代码保持不变 ...
        COMSelector.mainloop()

    else:
        sel_com = COMS[0] if COMS else None
    return sel_com if sel_com else "No target selected."

4. 验证延迟来源

单独测试COMScanner()的耗时,定位是扫描还是选择函数导致的延迟;在COMScanner()中添加打印语句,输出每个端口的处理时间,排查具体阻塞的端口。

内容的提问来源于stack exchange,提问作者Terabyte

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 21:25:22