Python子进程被terminate终止时未触发atexit函数的问题排查
问题分析与解决方案
为什么子进程被terminate()终止时不会生成foo.txt?
这个问题的核心差异在于Windows系统下进程终止方式的不同:
- 当你调用
proc.terminate()时,Python底层调用的是Windows API的TerminateProcess()函数,这是一种强制终止操作——它会直接杀死目标进程,不给进程执行任何退出清理代码(包括atexit注册的钩子函数)的机会,自然也就不会生成foo.txt。 - 而你单独运行
sub.py并手动终止(比如按Ctrl+C)时,Python会捕获SIGINT中断信号,触发正常的退出流程,此时atexit注册的函数会被执行,所以能正常生成文件。
如何让子进程被终止时仍生成foo.txt?
这里提供两种可靠的解决方案:
方案一:模拟Ctrl+C发送中断信号(最贴近手动终止的行为)
通过给子进程发送CTRL_C_EVENT信号,触发其正常退出流程。需要注意启动子进程时要创建独立的进程组:
## 修改后的mar.py import os import subprocess import time import signal print('MASTER PID: ', os.getpid()) # 创建新进程组,确保信号能正确送达子进程 proc = subprocess.Popen( ["D:\\Miniconda3\\python.exe", r"C:\\Users\\J\\Desktop\\test\\sub.py"], shell=False, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP ) def terminator(): # 发送Ctrl+C对应的中断信号,替代强制终止 proc.send_signal(signal.CTRL_C_EVENT) time.sleep(5) terminator()
sub.py无需修改,这样子进程收到信号后会执行atexit钩子,生成foo.txt。
方案二:通过IPC让子进程主动退出(更可控)
通过标准输入管道给子进程发送退出指令,让子进程主动结束循环并执行清理逻辑:
## 修改后的mar.py import os import subprocess import time print('MASTER PID: ', os.getpid()) # 开启标准输入管道用于进程间通信 proc = subprocess.Popen( ["D:\\Miniconda3\\python.exe", r"C:\\Users\\J\\Desktop\\test\\sub.py"], shell=False, stdin=subprocess.PIPE ) def terminator(): # 发送退出指令 proc.stdin.write(b'exit\n') proc.stdin.flush() proc.wait() # 等待子进程完全退出 time.sleep(1) terminator()
## 修改后的sub.py import atexit import time import os import sys print('SUB PID: ', os.getpid()) os.chdir("C:\\Users\\J\\Desktop\\test") def handle_exit(): with open("foo.txt", "w") as f: f.write("Life is too short, you need python") atexit.register(handle_exit) while True: print('alive') time.sleep(1) # 检查是否收到退出指令 if sys.stdin.readline().strip() == 'exit': break
这种方式更灵活,你可以在子进程退出前添加更多自定义的清理逻辑,完全由子进程掌控退出流程。
内容的提问来源于stack exchange,提问作者maynull
相关产品推荐
相关产品推荐

