python多进程程序如何优雅退出子进程

以下是优雅退出子进程的函数

    def signal_handler(sig, frame):
        print('Interrupt received, shutting down...')
        current_process = psutil.Process(os.getpid())
        for child in current_process.children(recursive=True):
            child.terminate()
        gone, still_alive = psutil.wait_procs(current_process.children(), timeout=5)
        for p in still_alive:
            p.kill()
        listener.stop()
        sys.exit(0)

函数解读:signal_handler(sig, frame)

这个函数的作用是捕获系统信号(如 SIGINTSIGTERM),并在收到信号后优雅地关闭当前进程及其所有子进程,确保资源释放和进程的正确退出。以下是对函数的详细分解和解读:


函数参数

  1. sig

    • 表示捕获的信号类型,例如:
      • SIGINT:通常由用户按下 Ctrl+C 触发。
      • SIGTERM:通常由系统发送,用于通知进程应该退出。
    • 这是信号处理函数的标准参数,用于标识具体的信号。
  2. frame

    • 表示当前信号触发时的栈帧信息。
    • 通常不需要直接使用,但可以用于调试或记录信号触发时的上下文。

函数逻辑分解

1. 打印收到中断信号的提示

print('Interrupt received, shutting down...')
  • 当进程收到信号时,向终端输出一条提示信息,表明程序正在关闭。
  • 这一步是为了让用户或系统知道进程正在处理退出信号。

2. 获取当前进程及其子进程

current_process = psutil.Process(os.getpid())
  • 使用 psutil 库获取当前进程对象。
    • os.getpid():获取当前进程的 PID(进程 ID)。
    • psutil.Process():通过 PID 创建一个 psutil 进程对象,用于管理或操作进程。
for child in current_process.children(recursive=True):
    child.terminate()
  • current_process.children(recursive=True)
    • 获取当前进程的所有直接或间接子进程(递归方式)。
    • 如果当前进程启动了多个子进程,这一步会返回所有子进程的列表。
  • child.terminate()
    • 对每个子进程调用 terminate() 方法,向子进程发送一个 SIGTERM 信号,通知其应该退出。
    • 这是优雅地终止子进程的第一步。

3. 等待子进程退出,处理未退出的进程

gone, still_alive = psutil.wait_procs(current_process.children(), timeout=5)
  • psutil.wait_procs()
    • 等待子进程退出。
    • 参数:
      • current_process.children():获取当前进程的所有直接子进程。
      • timeout=5:最多等待 5 秒,超时后继续执行。
    • 返回值:
      • gone:已成功退出的子进程列表。
      • still_alive:仍然存活的子进程列表。
for p in still_alive:
    p.kill()
  • 如果某些子进程在 5 秒内未退出(即仍然存活),调用 kill() 方法强制终止它们。
    • p.kill()
      • 向子进程发送 SIGKILL 信号,强制终止进程。
      • 这一步是确保所有子进程都被终止,不留下僵尸进程。

4. 停止监听器

listener.stop()
  • 这里的 listener 是一个日志监听器对象,负责集中管理日志记录。
  • 调用 listener.stop() 方法停止日志监听器,释放相关资源。
  • 注意:listener 对象必须在函数外部定义,并传递给 signal_handler,否则这里会报错。

5. 退出主进程

sys.exit(0)
  • sys.exit()
    • 退出当前进程。
    • 参数 0 表示正常退出(非错误状态)。
    • 在所有子进程和资源释放完成后,主进程调用这个方法彻底退出。

函数用途

这个函数的主要用途是处理关闭信号,确保以下几点:

  1. 优雅地退出子进程

    • 通过 terminate()kill() 方法,确保所有子进程都被正确关闭。
    • 避免子进程继续运行导致资源泄漏或僵尸进程问题。
  2. 释放资源

    • 停止日志监听器等资源,确保没有未释放的文件句柄或队列。
  3. 正常退出主进程

    • 在完成所有清理后,主进程以正常状态退出。

可能的改进

  1. 处理异常情况

    • 如果某些子进程在 terminate()kill() 时出现异常,可以捕获并记录日志。
  2. 动态传递 listener

    • 当前代码中 listener 是一个外部对象,未明确传递给 signal_handler。可以通过参数传递 listener,避免未定义错误。

总结

这个函数结合了 psutil 和信号处理机制,能够优雅地关闭主进程及其子进程,避免资源泄漏和僵尸进程问题,同时确保系统稳定性。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容