Python多进程:子进程崩溃?

8
1个回答

19

使用 multiprocessing.Pool 时,如果池中的一个子进程崩溃,您将完全不会收到通知,而新的进程将立即启动以取代它:

>>> import multiprocessing
>>> p = multiprocessing.Pool()
>>> p._processes
4
>>> p._pool
[<Process(PoolWorker-1, started daemon)>, <Process(PoolWorker-2, started daemon)>, <Process(PoolWorker-3, started daemon)>, <Process(PoolWorker-4, started daemon)>]
>>> [proc.pid for proc in p._pool]
[30760, 30761, 30762, 30763]

然后在另一个窗口中:

dan@dantop:~$ kill 30763

回到泳池:

>>> [proc.pid for proc in p._pool]
[30760, 30761, 30762, 30767]  # New pid for the last process

即使发生了意外,您仍然可以像往常一样继续使用池。但是,在被杀死的子进程死亡时正在运行的任何工作项都将不会完成或重新启动。如果您正在运行一个依赖于该工作项完成的阻塞mapapply调用,则可能会无限期挂起。这个问题已经在bug报告中提出,但问题仅在concurrent.futures.ProcessPoolExecutor中得到解决,而不是在multiprocessing.Pool中。从Python 3.3开始,ProcessPoolExecutor将引发BrokenProcessPool异常(如果子进程被杀死),并禁止进一步使用池。遗憾的是,multiprocessing没有获得此增强功能。目前,如果您想防止池调用由于子进程崩溃而永久阻塞,必须使用丑陋的解决方法

注意:上述仅适用于池中的进程实际上崩溃,这意味着该进程完全死亡。如果子进程引发异常,则在尝试检索工作项的结果时,该异常将向父进程传播:

>>> def f(): raise Exception("Oh no")
... 
>>> pool = multiprocessing.Pool()
>>> result = pool.apply_async(f)
>>> result.get()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/multiprocessing/pool.py", line 528, in get
    raise self._value
Exception: Oh no

如果直接使用 multiprocessing.Process,当进程崩溃时,进程对象会显示该进程已经以非零退出码退出:

>>> def f(): time.sleep(30)
... 
>>> p = multiprocessing.Process(target=f)
>>> p.start()
>>> p.join()  # Kill the process while this is blocking, and join immediately ends
>>> p.exitcode
-15

如果出现异常,行为类似:

from multiprocessing import Process

def f(x):
    raise Exception("Oh no")

if __name__ == '__main__':
    p = Process(target=f)
    p.start()
    p.join()
    print(p.exitcode)
    print("done")

输出:

Process Process-1:
Traceback (most recent call last):
  File "/usr/lib/python3.2/multiprocessing/process.py", line 267, in _bootstrap
    self.run()
  File "/usr/lib/python3.2/multiprocessing/process.py", line 116, in run
    self._target(*self._args, **self._kwargs)
TypeError: f() takes exactly 1 argument (0 given)
1
done

正如您所看到的,子进程的回溯被打印出来,但它并不影响主进程的执行,主进程能够显示子进程的exitcode1


2
看起来这个漏洞仍然存在,并且有一些未完成的尝试来修复它,最新的尝试是 https://github.com/python/cpython/pull/16103。 - astrojuanlu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接