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

父进程终止时避免子进程存活:Flask+multiprocessing部署问题求助

解决Flask应用重启后multiprocessing子进程残留的问题

这确实是在Web服务中用multiprocessing跑后台长任务的常见坑——当Flask父进程因为部署重启被终止时,子进程会脱离父进程的管理,变成系统接管的孤儿进程,继续占用CPU、内存等资源。下面给你几个实用的解决方案,你可以根据自己的场景选择:

方案1:子进程主动监听父进程状态

让子进程定期检查父进程是否存活,一旦发现父进程已经终止,就主动退出。这种方法轻量无依赖,适合简单场景。

修改你的function_wrapper函数,加入父进程存活检查逻辑:

import os
import time

def function_wrapper(self, parameters, config, process_dir):
    parent_pid = os.getppid()
    # 可根据任务类型调整检查间隔,比如10秒一次
    check_interval = 10
    
    while True:
        # 发送0号信号判断父进程是否存活:该信号不会执行任何操作,仅用于检测进程存在性
        try:
            os.kill(parent_pid, 0)
        except OSError:
            # 父进程已终止,子进程主动退出
            print(f"父进程{parent_pid}已终止,子进程{os.getpid()}即将退出")
            return
        
        # 执行你的原有业务逻辑
        # ... 这里写你原本的长时任务代码 ...
        
        # 循环任务可加休眠避免空转;单次任务可将检查放在任务间隙
        time.sleep(check_interval)

优点:实现简单,无额外依赖,跨平台可用
缺点:子进程退出有延迟,需等到下一次检查周期才能发现父进程已死

方案2:利用进程组批量终止子进程(Unix/Linux专属)

在Unix/Linux系统中,可通过进程组将父进程和所有子进程绑定,当父进程收到终止信号时,一次性杀掉整个进程组的所有进程。

步骤1:启动子进程时设置进程组

修改你启动子进程的代码,添加preexec_fn参数创建新进程组:

import os
from multiprocessing import Process

def set_process_group():
    # 创建新进程组,让子进程成为进程组首进程
    os.setpgrp()

def execute(self, process_id):
    self.__process_id = process_id
    process_dir = self.__dependencies["process_dir"]
    self.fit_dependencies()
    process = Process(
        target=self.function_wrapper,
        name=process_id,
        args=(self.__parameters, self.__config, process_dir,),
        preexec_fn=set_process_group  # Unix系统下生效
    )
    process.start()

步骤2:父进程监听终止信号,杀掉整个进程组

在Flask应用初始化处,添加信号处理逻辑,当父进程收到SIGTERM(部署重启常用终止信号)或SIGINT(Ctrl+C)时,终止整个进程组:

import signal
import os
import sys
from flask import Flask

app = Flask(__name__)

def handle_shutdown(signum, frame):
    try:
        # 获取当前进程所在的进程组ID
        pgid = os.getpgid(os.getpid())
        # 向整个进程组发送终止信号
        os.killpg(pgid, signal.SIGTERM)
    except OSError:
        # 避免无子女进程时抛出异常
        pass
    # 正常退出父进程
    sys.exit(0)

# 注册信号处理函数
signal.signal(signal.SIGTERM, handle_shutdown)
signal.signal(signal.SIGINT, handle_shutdown)

优点:子进程能立即被终止,无延迟,适合Unix环境生产场景
缺点:Windows系统不支持进程组,无法使用此方案

方案3:使用专业任务队列(生产环境推荐)

如果你的Web应用有大量长时任务需要管理,推荐使用专门的任务队列框架(比如Celery),将任务从Flask进程中剥离,交给独立的Worker进程处理。这样Flask重启时,Worker进程不受影响,还能实现任务监控、重试、调度等高级功能。

简单示例:

  1. 安装Celery和消息中间件(比如Redis):
pip install celery redis
  1. 配置Celery任务:
# celery_tasks.py
from celery import Celery

# 初始化Celery,用Redis作为消息中间件和结果存储
app = Celery('my_tasks', broker='redis://localhost:6379/0', backend='redis://localhost:6379/0')

@app.task
def long_running_task(parameters, config, process_dir):
    # 这里写你的长时任务逻辑
    # ...
  1. 在Flask应用中提交任务:
from celery_tasks import long_running_task

def execute(self, process_id):
    self.__process_id = process_id
    process_dir = self.__dependencies["process_dir"]
    self.fit_dependencies()
    # 异步提交任务到Celery Worker
    long_running_task.delay(self.__parameters, self.__config, process_dir)
  1. 启动Celery Worker:
celery -A celery_tasks worker --loglevel=info

优点:任务管理能力强,支持重试、定时任务、监控等,适合复杂生产场景
缺点:需额外部署消息中间件和Worker进程,学习成本稍高


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:39:07