当Python脚本打开子进程,并且其中一个进程崩溃时会发生什么?
https://dev59.com/_3XYa4cB1Zd3GeqP8qMq#18216437
主进程会崩溃吗?
其他子进程会崩溃吗?
是否有信号或其他事件被传播?
当Python脚本打开子进程,并且其中一个进程崩溃时会发生什么?
https://dev59.com/_3XYa4cB1Zd3GeqP8qMq#18216437
主进程会崩溃吗?
其他子进程会崩溃吗?
是否有信号或其他事件被传播?
使用 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
即使发生了意外,您仍然可以像往常一样继续使用池。但是,在被杀死的子进程死亡时正在运行的任何工作项都将不会完成或重新启动。如果您正在运行一个依赖于该工作项完成的阻塞map
或apply
调用,则可能会无限期挂起。这个问题已经在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
正如您所看到的,子进程的回溯被打印出来,但它并不影响主进程的执行,主进程能够显示子进程的exitcode
为1
。