python孤儿进程示例和解决方法
子进程原本的父进程停了,它就会由系统的init进程接管,这个子线程就成了孤儿进程,另外还有个僵尸进程的概念,说的是进程已经结束了但是资源没有被父进程清理。
在python中很容易产生孤儿进程,例如以下代码:
import timeimport multiprocessingdef 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 timeimport multiprocessingdef 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() 进行检测
Tags: