当主进程由于未处理的异常而结束时退出子进程

时间:2018-07-09 06:16:55

标签: python exception multiprocessing hook

我有一个可能因异常而终止的python服务-我可以接受。但是问题在于它催生了一个子进程,即使其父进程失败了,该子进程仍继续运行。

import multiprocessing as mp
from time import sleep
import os
import atexit
import psutil

@atexit.register     # Doesn't fired at exception
def goodbye():
    current_process = psutil.Process()
    children = current_process.children(recursive=True)
    for child in children:
        print('Kill child with pid {}'.format(child.pid))
        try:
            child.terminate()
        except:
            pass
    print("You are now leaving the Python sector.")


def func():         # Child process
    while True:
        ppid = os.getppid()
        print("Parent process id:", ppid)
        if ppid == 1:
            print("Parent process has terminated")
            break
        sleep(1)

t = mp.Process(target=func, args=())
t.start()

print(9 + "0")      # Exception here
print("I'm ok")

服务将继续(正式)运行,直到获得外界的认可:

Parent process id: 29118
Traceback (most recent call last):
  File "stestcp.py", line 32, in <module>
    print(9 + "0")      # Exception here
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Parent process id: 29118
Parent process id: 29118
Parent process id: 29118
Parent process id: 29118
Parent process id: 29118
^CError in atexit._run_exitfuncs:
Traceback (most recent call last):
  File "/usr/lib/python3.6/multiprocessing/popen_fork.py", line 28, in poll
Process Process-1:
    pid, sts = os.waitpid(self.pid, flag)
KeyboardInterrupt
Traceback (most recent call last):
  File "/usr/lib/python3.6/multiprocessing/process.py", line 258, in _bootstrap
    self.run()
  File "/usr/lib/python3.6/multiprocessing/process.py", line 93, in run
    self._target(*self._args, **self._kwargs)
  File "stestcp.py", line 27, in func
    sleep(1)
KeyboardInterrupt
Kill child with pid 29119
You are now leaving the Python sector.

问题是-当程序因异常而失败时,是否有任何方法可以调用某些全局回退函数(例如atexit)?

1 个答案:

答案 0 :(得分:0)

感谢Andrej Kesely,找到了解决方案。现在的工作示例如下:

import multiprocessing as mp
from time import sleep
import sys
import os
import atexit
import psutil

class ExitHooks(object):
    def __init__(self):
        self.exit_code = None
        self.exception = None

    def hook(self):
        self._orig_exit = sys.exit
        sys.exit = self.exit
        sys.excepthook = self.exc_handler

    def exit(self, code=0):
        self.exit_code = code
        self._orig_exit(code)

    def exc_handler(self, exc_type, exc, *args):   # Called at exception
        self.exception = exc
        goodbye()

hooks = ExitHooks()
hooks.hook()

@atexit.register     # Doesn't fired at exception
def goodbye():
    if hooks.exit_code is not None:
        print("death by sys.exit(%d)" % hooks.exit_code)
    elif hooks.exception is not None:
        print("death by exception: %s" % hooks.exception)
    else:
        print("natural death")
    current_process = psutil.Process()
    children = current_process.children(recursive=True)
    for child in children:
        print('Kill child with pid {}'.format(child.pid))
        try:
            child.terminate()
        except:
            pass
    print("You are now leaving the Python sector.")


def func():         # Child process
    while True:
        ppid = os.getppid()
        print("Parent process id:", ppid)
        if ppid == 1:
            print("Parent process has terminated")
            break
        sleep(1)

t = mp.Process(target=func, args=())
t.start()

sleep(2)
print(9 + "0")      # Exception here