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

PyWebView打包exe后多进程启动MCP服务器导致程序无限重启的解决方法

PyWebView打包exe后多进程启动MCP服务器导致程序无限重启的解决方法

我来帮你搞定这个无限重启的问题——这其实是PyInstaller打包多进程Python程序在Windows环境下的典型坑,根源在于子进程的创建逻辑触发了主程序UI的重复启动。下面一步步给你讲清楚原因和解决办法:

问题根源

在Windows系统中,PyInstaller打包后的exe使用multiprocessing时,默认采用spawn方式创建子进程。这种方式会重新加载并执行整个脚本文件,如果没有正确的入口保护,子进程会再次运行main()函数,从而重复创建PyWebView窗口,形成无限循环。

具体解决步骤

1. 添加多进程打包支持(最关键)

在你的main()函数开头或者入口代码块中,必须调用multiprocessing.freeze_support()——这是PyInstaller专门为打包多进程程序提供的修复,它会告诉子进程跳过主程序入口的执行逻辑,只运行指定的target函数。

2. 替换不可靠的__del__清理逻辑

Python的__del__方法执行时机不确定,程序退出时可能不会被及时调用,导致MCP进程无法正常终止。建议改用PyWebView的窗口关闭事件来处理进程清理,更可靠。

3. 调整PyInstaller打包命令

确保打包时包含所有依赖的资源文件(比如UI的HTML、图标),避免运行时找不到文件。

修改后的完整代码示例

调整PyWebView主程序部分

import multiprocessing
import webview
import logging
# 导入你的MCP相关代码
from your_mcp_file import FastMCP

class CommandsAutomatorApi:
    def init_mcp(self, mcp_runner):
        self.mcp_process = multiprocessing.Process(target=mcp_runner.run_mcp)
        self.mcp_process.start()

class MCPRunner:
    def run_mcp(self):
        # 子进程内单独初始化MCP对象,避免全局实例导致的重复加载问题
        mcp = FastMCP("resume_fetcher")
        
        @mcp.tool()
        async def get_files():
            return 'using tool'
            
        # 配置并启动MCP服务器
        mcp.settings.mount_path = "/mcp"
        mcp.settings.port = 8765
        mcp.settings.host = "127.0.0.1"
        logging.debug(f"Starting MCP server at http://{mcp.settings.host}:{mcp.settings.port}{mcp.settings.mount_path}")
        mcp.run(transport="streamable-http")

def main():
    # 必须先调用freeze_support,处理PyInstaller打包后的多进程问题
    multiprocessing.freeze_support()
    
    api = CommandsAutomatorApi()
    mcp_runner = MCPRunner()
    api.init_mcp(mcp_runner)
    
    # 定义窗口关闭时的清理逻辑,比__del__更可靠
    def on_window_closed(window):
        if hasattr(api, 'mcp_process') and api.mcp_process.is_alive():
            api.mcp_process.terminate()
            api.mcp_process.join()
            logging.info("MCP server process terminated successfully")
    
    # 创建WebView窗口并绑定关闭事件
    window = webview.create_window(
        'Commands Automator', 
        'ui/commands_automator.html', 
        js_api=api, 
        width=1000, 
        height=800,
        icon='ui/resources/Commands_Automator.ico'
    )
    window.events.closed += on_window_closed
    
    webview.start()

if __name__ == '__main__':
    main()

关键修改说明

  • MCP对象移到子进程内初始化:原来的全局MCP实例会在子进程启动时被重复加载,移到run_mcp方法内可以避免这个问题,确保每个子进程有独立的MCP实例。
  • 添加窗口关闭清理逻辑:替代不可靠的__del__方法,确保UI关闭时MCP进程能被及时终止。

正确的PyInstaller打包命令

假设你的主脚本名为app.py,资源都在ui文件夹下,使用以下命令打包:

pyinstaller --onefile --windowed --add-data "ui;ui" --name CommandsAutomator app.py
  • --onefile:打包成单个exe文件
  • --windowed:不显示控制台窗口(如果不需要调试可以添加)
  • --add-data "ui;ui":将本地的ui文件夹打包到exe中,运行时会自动解压到临时目录,保持路径一致性

为什么这些修改有效

  • freeze_support()会在子进程启动时拦截入口代码的执行,只运行multiprocessing.Process指定的run_mcp目标函数,不会重复启动UI窗口。
  • 子进程内单独初始化MCP对象,避免了全局变量导致的资源冲突和重复加载问题。
  • 窗口关闭事件的清理逻辑能确保MCP进程在UI关闭时被及时终止,避免残留后台进程。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 10:24:33