python 如何优雅地退出子进程
主进程产生子进程,子进程进入永久循环模式。当主进程要求子进程退出时,如何能安全地退出子进程呢?
参考一些代码,我写了这个例子。运行之后,用kill pid试试。pid是主进程的pid。当然子进程的也没问题。
1)如果kill pid为子进程的pid,当所有子进程都kill掉了,主进程就关闭了。这也是我们想要的结果。
2)如果kill pid为主进程的pid,主进程向子进程发送退出信号,然后等全部子进程关闭后退出。
保证了主进程在所有子进程退出之后退出。
#-*- coding: UTF-8 -*- # graceful_exit_event.py # UTF-8 without BOM # # refer: # http://*.com/questions/26414704/how-does-a-python-process-exit-gracefully-after-receiving-sigterm-while-waiting?rq=1 # http://www.cnblogs.com/kaituorensheng/p/4445418.html # init created: 2016-07-13 # last updated: 2016-07-14 # ####################################################################### import os import signal import multiprocessing class GracefulExitException(Exception): @staticmethod def sigterm_handler(signum, frame): raise GracefulExitException() pass class GracefulExitEvent(object): def __init__(self): self.workers = [] self.exit_event = multiprocessing.Event() # Use signal handler to throw exception which can be caught # by worker process to allow graceful exit. signal.signal(signal.SIGTERM, GracefulExitException.sigterm_handler) pass def reg_worker(self, wp): self.workers.append(wp) pass def is_stop(self): return self.exit_event.is_set() def notify_stop(self): self.exit_event.set() def wait_all(self): while True: try: for wp in self.workers: wp.join() print "main process(%d) exit." % os.getpid() break except GracefulExitException: self.notify_stop() print "main process(%d) got GracefulExitException." % os.getpid() except Exception, ex: self.notify_stop() print "main process(%d) got unexpected Exception: %r" % (os.getpid(), ex) break pass ####################################################################### def worker_proc(gee): import sys, time print "worker(%d) start ..." % os.getpid() try: while not gee.is_stop(): # do task job here print ".", time.sleep(1) else: print "" print "worker process(%d) got exit event." % os.getpid() print "worker process(%d) do cleanup..." % os.getpid() time.sleep(1) print "[%d] 3" % os.getpid() time.sleep(1) print "[%d] 2" % os.getpid() time.sleep(1) print "[%d] 1" % os.getpid() except GracefulExitException: print "worker(%d) got GracefulExitException" % os.getpid() except Exception, ex: print "Exception:", ex finally: print "worker(%d) exit." % os.getpid() sys.exit(0) if __name__ == "__main__": import sys print "main process(%d) start" % os.getpid() gee = GracefulExitEvent() # Start some workers process and run forever for i in range(0, 10): wp = multiprocessing.Process(target=worker_proc, args=(gee,)) wp.start() gee.reg_worker(wp) gee.wait_all() sys.exit(0)
需要说明的是:
如果某个进程接受 kill pid,必须在该进程函数的最外层捕获: except GracefulExitException,
同时该进程函数内部调用捕获了 except Excetion,则必须在之前捕获GracefulExitException,
即:
(2) 必须在(3)之前。否则同时去掉(2)和(3)。
def do_some_func(): try: time.sleep(10) blablabla (1) except SomeError: # 捕获特定的异常 pass (2) except GracefulExitException: # 接收 kill pid pass (3) except: # 不建议捕获默认异常, 否则必须在此之前捕获:GracefulExitException pass def some_process_main(gee): try: while not gee.is_stop(): do_some_func() except GracefulExitException: # 接收到了 kill pid, 设置中止循环 gee.notify_stop() pass except: pass