确保子进程在退出Python程序时结束

83 投票
15 回答
91434 浏览
提问于 2025-04-11 18:45

有没有办法确保在Python程序退出时,所有创建的子进程都已经结束?这里的子进程是指用subprocess.Popen()创建的那些。

如果没有的话,我是不是应该先尝试结束所有的进程,然后再用kill -9强制结束?有没有更简单的方法?

15 个回答

16

subprocess.Popen.wait() 是确保子进程已经结束的唯一方法。实际上,POSIX 操作系统要求你必须等待你的子进程结束。如果你不等待,很多类 Unix 系统会产生一个“僵尸”进程:也就是一个已经结束但父进程没有等待的子进程。

如果子进程写得比较好,它会正常结束。通常,子进程会从管道(PIPE)中读取数据。关闭输入是一个很好的提示,告诉子进程该结束了。

如果子进程有问题没有结束,你可能需要强制结束它。你应该修复这个问题。

如果子进程是一个“永远运行”的循环,并且设计上不打算结束,你要么杀掉它,要么提供一些输入或消息,强制它结束。


补充说明。

在标准操作系统中,你可以使用 os.kill( PID, 9 ) 来结束进程。需要注意的是,杀死进程的方式 -9 是比较粗暴的。如果能用 SIGABRT (6) 或 SIGTERM (15) 来结束,那就显得更礼貌一些。

在 Windows 操作系统中,没有一个有效的 os.kill 方法。你可以查看这个 ActiveState 的教程,了解如何在 Windows 中结束一个进程。

我们有一些子进程是 WSGI 服务器。为了结束它们,我们会对一个特殊的 URL 发送 GET 请求;这样会让子进程进行清理并退出。

53

在*nix系统中,使用进程组可能会对你有所帮助——这样你可以捕捉到由你的子进程产生的子进程。

if __name__ == "__main__":
  os.setpgrp() # create new process group, become its leader
  try:
    # some code
  finally:
    os.killpg(0, signal.SIGKILL) # kill all processes in my group

另一个需要考虑的事情是升级信号:从SIGTERM(这是kill的默认信号)到SIGKILL(也就是kill -9)。在发送信号之间稍等一会儿,这样可以给进程一个机会,干净地退出,然后再用kill -9

63

你可以使用atexit这个模块来处理程序退出时需要执行的清理任务。

atexit.register(func[, *args[, **kargs]])

在你的清理过程中,你还可以自己设置等待时间,并在达到你设定的超时时间时终止它。

>>> import atexit
>>> import sys
>>> import time
>>> 
>>> 
>>>
>>> def cleanup():
...     timeout_sec = 5
...     for p in all_processes: # list of your processes
...         p_sec = 0
...         for second in range(timeout_sec):
...             if p.poll() == None:
...                 time.sleep(1)
...                 p_sec += 1
...         if p_sec >= timeout_sec:
...             p.kill() # supported from python 2.6
...     print 'cleaned up!'
...
>>>
>>> atexit.register(cleanup)
>>>
>>> sys.exit()
cleaned up!

注意 -- 如果这个进程(父进程)被杀掉,注册的函数将不会被执行。

对于Python版本大于等于2.6,以下Windows方法不再需要

这是在Windows中终止一个进程的方法。你的Popen对象有一个pid属性,所以你可以通过success = win_kill(p.pid)来调用它(需要安装pywin32):

    def win_kill(pid):
        '''kill a process by specified PID in windows'''
        import win32api
        import win32con

        hProc = None
        try:
            hProc = win32api.OpenProcess(win32con.PROCESS_TERMINATE, 0, pid)
            win32api.TerminateProcess(hProc, 0)
        except Exception:
            return False
        finally:
            if hProc != None:
                hProc.Close()

        return True

撰写回答