引言
在开发需要管理子进程的应用程序时(例如任务调度系统、自动化测试平台等),我们常会遇到需要终止整个进程树的需求。直接使用kill -9
虽然暴力有效,但可能导致资源泄漏或状态不一致等问题。如何在Python中实现多进程任务的生命周期管理?本文将通过完整的代码案例+原理解析+可视化流程图,带你掌握三个关键技能:
- 创建多线程任务并监控输出
- 捕获进程树关系
- 分级终止策略(优雅退出→强制终止)
一、多线程脚本(task.py):工作线程的产生 ,子进程
import os
import threading
def print_msg():
for count in range(100000):
current_thread = threading.current_thread()
print(f"task:{
os.getpid()} {
current_thread.name} {
current_thread.ident} count:{
count}", flush=True)
if __name__ == '__main__':
threads = []
for i in range(3):
thread = threading.Thread(
target=print_msg,
name=f"task_thread-{
i + 1}"
)
threads.append(thread)
thread.start()
# 等待所有线程完成
for thread in threads:
thread.join()
print("所有工作线程执行完毕") # 如果外部终止,这里不会输出
关键点解析:
- 每个线程执行10,000次计数打印,
flush=True
确保实时输出 - 主线程会等待所有工作线程结束(但实际需要外部终止)
- 线程共享进程PID,体现多线程特性
二、进程监控脚本(runner.py):子进程的创建与输出捕获 ,父进程
import os
import subprocess
if __name__ == '__main__':
try:
# 使用上下文管理器启动子进程
with subprocess.Popen(["python3", "task.py"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, encoding='utf-8') as process:
# 记录子进程pid到文件
with open("pid.txt", "w") as f:
f.write(str(process.pid))
# 实时读取子进程输出
while True:
line = process.stdout.readline()
# 如果没有下两面这句,kill的又是task_pid,那么task进程会变成僵尸进程,无法kill
# if not line and process.poll() is not None: # 输出结束且子进程已退出
# break
print(f"runner:{
os.getpid()} popen:{
process.pid} {
line.strip()}")
except KeyboardInterrupt:
print