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

Python使用subprocess时如何在任意子进程结束时杀死所有子进程

任意子进程退出时自动终止其余子进程的实现方案

原代码的核心问题是使用串行proc.wait()逻辑,会阻塞在第一个子进程的等待上,无法实时感知其他子进程的退出状态,因此没法触发全局终止逻辑。

实现逻辑

  • 保留原有set_pdeathsig兜底逻辑,避免父进程意外退出时残留孤儿进程
  • 放弃串行阻塞等待,改用低频率轮询非阻塞检查所有子进程的运行状态
  • 一旦检测到任意子进程退出(无论正常结束还是异常被杀死),立刻遍历终止所有剩余存活子进程,先发送SIGTERM给进程留优雅退出时间,超时未退出再发送SIGKILL强杀,最后回收所有进程资源避免僵尸进程
  • 不依赖子进程侧添加任何代码,即使子进程被SIGKILL强杀无法执行自定义逻辑,父进程也能正常检测到退出事件,可靠性远高于子进程主动杀父进程的方案

可直接运行的修正代码

import ctypes
import os
import signal
import subprocess
import time

libc = ctypes.CDLL("libc.so.6")


def set_pdeathsig(sig=signal.SIGTERM):
    def callable():
        return libc.prctl(1, sig)
    return callable


def cleanup_all_procs(proc_list):
    # 先发送SIGTERM,给进程留优雅退出的时间
    for proc in proc_list:
        if proc.poll() is None:
            try:
                proc.terminate()
            except Exception:
                pass
    # 等待3秒,仍未退出的进程强制SIGKILL
    time.sleep(3)
    for proc in proc_list:
        if proc.poll() is None:
            try:
                proc.kill()
            except Exception:
                pass
    # 回收所有进程资源,避免僵尸进程
    for proc in proc_list:
        try:
            proc.wait(timeout=1)
        except Exception:
            pass


if __name__ == "__main__":
    procs = []
    for i in range((os.cpu_count() * 2) - 1):
        proc = subprocess.Popen(
            ['python', "pythonscript_i_need_to_run/"],
            preexec_fn=set_pdeathsig(signal.SIGTERM)
        )
        procs.append(proc)
    procs.append(subprocess.Popen(
        ["python", "other_pythonscript_i_need_to_run"],
        preexec_fn=set_pdeathsig(signal.SIGTERM)
    ))

    try:
        while True:
            # 非阻塞检查所有子进程状态
            for proc in procs:
                if proc.poll() is not None:
                    # 检测到任意进程退出,触发全局清理
                    cleanup_all_procs(procs)
                    exit(0)
            # 100ms检查一次,CPU占用可忽略,可根据实时性要求调整间隔
            time.sleep(0.1)
    except KeyboardInterrupt:
        # 捕获Ctrl+C手动中断场景,同样清理所有子进程
        cleanup_all_procs(procs)

之前方案失效的原因说明

  • 子进程内调用os.kill(os.getppid(), signal.SIGTERM)不生效:一是子进程如果被SIGKILL强杀,根本没有机会执行这行代码;二是如果父进程提前退出,子进程会被init进程接管,getppid()拿到的不是原始父进程PID;三是父进程可能存在信号捕获/忽略逻辑,导致SIGTERM没有触发退出。
  • 进程组方案不生效:大概率是启动子进程时没有正确创建/加入独立进程组,子进程默认继承父进程的进程组,直接杀进程组会把父进程本身也纳入杀死范围,逻辑容易出现冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 11:30:57