本文介绍一种简洁稳健的方案,使用 subprocess 直接管理两个外部可执行文件的生命周期,并通过 multiprocessing.Event 实现进程间协调,避免序列化错误与资源竞争,确保 process1.exe 在 process2.exe 退出时被及时终止。

先说说这类问题的核心思路。在Python脚本里同时控制两个外部exe进程,比如process1.exe和process2.exe,最头疼的地方在于:进程的生命周期必须强耦合在一起。换言之,process2.exe一旦异常退出,process1.exe必须立刻跟着终止,所有后续的检查逻辑也得同步停掉,不能拖泥带水。

原方案试图用multiprocessing.Process封装psutil的进程探测逻辑,看起来挺巧妙,但实际操作中却踩了坑——传递不可序列化的对象(比如psutil.Process实例、weakref等)直接引发了TypeError: cannot pickle 'weakref.ReferenceType' object。这个错误在Windows上特别常见,因为spawn启动方式对跨进程对象序列化有硬性限制,简单说就是:子进程拿不到主进程里那些复杂的东西。

那怎么破?根本解法其实就一句话:别在子进程里传复杂对象,改用更轻量、更可控的协作模型。

✅ 推荐方案:主进程直接管控,事件驱动协同

核心思想很直接:主进程亲自启动并持有两个subprocess.Popen实例,用process2.wait()阻塞等待它结束(这个调用天然支持异常退出,无论是正常关闭还是崩溃),然后主动干掉process1。同时,把耗时的检查逻辑(比如checkStatus)放到独立子进程里,用Event通知它提前退出。这样一来,所有操作都在主进程的掌控之中,既避免了序列化错误,也杜绝了竞态条件。

优化后的代码长这样:

import subprocess
import multiprocessing
import time

def checkStatus(event, queue):
    # 示例:模拟耗时检查逻辑(如轮询日志、验证端口、读取共享内存等)
    for i in range(10):
        if event.is_set():  # 主进程已发出终止信号
            print("checkStatus: 接收到终止信号,提前退出")
            break
        print(f"checkStatus: 执行第 {i+1} 次检查...")
        time.sleep(1)
    else:
        print("checkStatus: 检查完成")
    queue.put("checkStatus Completed!")
    # 注意:此处不再调用 event.wait(),避免死锁 —— 终止信号由主进程单向发出

def main():
    # 启动两个进程(注意路径和权限)
    process1 = subprocess.Popen(["./process1.exe"])
    process2 = subprocess.Popen(
        ["./process2.exe"], 
        creationflags=subprocess.CREATE_NEW_CONSOLE
    )

    # 创建通信机制
    queue = multiprocessing.Queue()
    event = multiprocessing.Event()

    # 启动检查子进程
    p_check = multiprocessing.Process(target=checkStatus, args=(event, queue))
    p_check.start()

    try:
        # 关键:阻塞等待 process2 结束(无论正常退出或崩溃)
        process2.wait()
        print("process2.exe 已退出,准备终止 process1.exe...")

        # 立即终止 process1
        process1.terminate()
        try:
            process1.wait(timeout=5)  # 等待优雅退出
        except subprocess.TimeoutExpired:
            process1.kill()  # 强制终止
            print("process1.exe 未响应,已强制杀死")

        # 通知检查进程终止
        event.set()

        # 等待检查进程结束
        p_check.join(timeout=2)
        if p_check.is_alive():
            p_check.terminate()
            p_check.join()
    finally:
        # 清理队列输出
        while not queue.empty():
            print(queue.get_nowait())

if __name__ == "__main__":
    main()

⚠️ 关键注意事项

整体来看,这个方案用最小依赖、最简逻辑实现了目标:process2的生命周期完全主导整个流程,主进程充当协调中枢,子进程只承担单一职责,彻底规避了序列化陷阱与竞态条件。 既干净又务实,值得在实际项目中直接落地。

本文转载于:https://www.php.cn/faq/2812608.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。