Python多进程:子进程崩溃?

9 投票
1 回答
9649 浏览
提问于 2025-04-18 14:18

当一个Python脚本打开了子进程,而其中一个进程崩溃了,会发生什么呢?

https://stackoverflow.com/a/18216437/311901

主进程会崩溃吗?

其他的子进程会崩溃吗?

有没有信号或者其他事件会被传递出去?

1 个回答

20

当你使用 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

撰写回答