python孤儿进程示例和解决方法

子进程原本的父进程停了,它就会由系统的init进程接管,这个子线程就成了孤儿进程,另外还有个僵尸进程的概念,说的是进程已经结束了但是资源没有被父进程清理。

在python中很容易产生孤儿进程,例如以下代码:

import time
import multiprocessing

def sub_task():
"""子进程任务:循环打印时间和父进程状态"""
while True:
print(f"{time.asctime()}, {multiprocessing.parent_process().is_alive()}")
time.sleep(1)

def parent_task():
"""父进程任务:开启一个循环打印的子进程,然后睡5秒"""
sub_p = multiprocessing.Process(target=sub_task, daemon=True)
sub_p.start()
time.sleep(5) # 将这里的5s修改为2s试试


if __name__ == "__main__":
p = multiprocessing.Process(target=parent_task, daemon=False)
p.start()

# 创建“父进程”后主进程睡3秒然后停掉“父进程”
time.sleep(3)

p.terminate()
p.join()
p.close()

上述的代码执行起来就会有问题,你会发现,即使已经将子进程设置为守护进程(daemon=True),将其父进程结束后,该子进程仍然在在打印,但是父进程状态(is_alive())由原来的 True 变为了 False,该进程已经成为了孤儿进程

但是,如果你将上述父进程休眠时间从5秒修改为2秒,则子进程也会随着父进程的退出而退出。

上述代码运行后,通过实验或任务管理器你会发现,父进程确实是在3秒后直接终止了,而子进程却没有。问题就在于父进程是正常退出还是异常退出,当父进程在执行terminate()之前已经执行完了(设置2s的休眠时间),则为正常退出,此时父线程会自动清理其子进程。而如果在执行terminate()的时候父进程仍然在执行(sleep() 设置为5)则为异常退出,此时它还没来得及清理其子进程,故而其子进程成为了孤儿进程

一个比较好的解决方法是使用进程间通信,例如使用 multiprocessing.Event

import time
import multiprocessing

def sub_task():
"""子进程任务:循环打印一个东西"""
while True:
print(f"{time.asctime()}, {multiprocessing.parent_process().is_alive()}")
time.sleep(1)

def parent_task(shutdown_flag):
"""父进程任务:开启一个循环打印的子进程,然后睡5秒"""
sub_p = multiprocessing.Process(target=sub_task, daemon=True)
sub_p.start()

shutdown_flag.wait()


if __name__ == "__main__":
shutdown_flag = multiprocessing.Event()
p = multiprocessing.Process(target=parent_task, args=(shutdown_flag,), daemon=False)
p.start()

# 创建“父进程”后睡3秒然后停掉“父进程”
time.sleep(3)

shutdown_flag.set()
p.join()
p.close()

print("main done")

multiprocessing.Event() 可以理解为一个bool值,有四个方法:

  • set():将bool设置为True
  • is_set():检测bool值是否为True
  • clear():将bool重新设置为False
  • wait():等待bool设置为True,等价于使用死循环调用 is_set() 进行检测

Leave a Comment