Python 在嵌套 multiprocessing.Process 上调用 start 后未引发异常

Python not raising exception after calling start on a nested multiprocessing.Process

我 运行 遇到程序中的一个问题,在启动嵌套子进程后没有抛出异常。也就是说,我有一个方法,我 运行 在 multiprocessing.Process 中。在该进程(“第一个进程”)内,我启动了另一个 multiprocessing.Process。在第二个进程上调用 start 后,第一个进程继续但不引发异常,而是在引发异常时挂起。

我无法判断这是预期的行为还是我偶然发现了 Python 中的错误。这是演示该问题的最小示例:

import multiprocessing as mp
import time

def spin_proc():
    while True:
        print("spin")
        time.sleep(1)

def issue():
    sub_proc = mp.Process(target=spin_proc)
    sub_proc.start()
    print("Exception called next.") # This text is printed, as expected.
    raise Exception() # This exception is never raised.
    print("This text is not printed.")

first_proc = mp.Process(target=issue)
first_proc.start()

输出为:

$ python3 except.py 
Exception called next.
spin
spin
spin
spin

它会无限期地继续打印“spin”,直到我按下 Ctrl-C。我本以为会引发异常并使程序崩溃。

我已在 Python 3.8.10 和 Python 3.9.2 中确认此行为。

这是预期的行为吗?如果是这样,为什么?它具有在我的代码中隐藏异常并意外阻塞的效果,这在尝试调试新内容但看不到任何输出时非常令人沮丧。如果它看起来是一个错误,请告诉我,我会将其报告给 Python 错误跟踪器。

谢谢!这是按 Ctrl-C 时的输出。它似乎想抛出异常,但由于某种原因挂起:

^CError in atexit._run_exitfuncs:
Traceback (most recent call last):
  File "/usr/lib/python3.8/multiprocessing/popen_fork.py", line 27, in poll
    pid, sts = os.waitpid(self.pid, flag)
KeyboardInterrupt
Process Process-1:1:
Process Process-1:
Traceback (most recent call last):
  File "/usr/lib/python3.8/multiprocessing/process.py", line 315, in _bootstrap
    self.run()
  File "/usr/lib/python3.8/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "except.py", line 7, in spin_proc
    time.sleep(1)
KeyboardInterrupt
Traceback (most recent call last):
  File "/usr/lib/python3.8/multiprocessing/process.py", line 315, in _bootstrap
    self.run()
  File "/usr/lib/python3.8/multiprocessing/process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "except.py", line 13, in issue
    raise Exception() # This exception is never raised.
Exception

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.8/multiprocessing/process.py", line 318, in _bootstrap
    util._exit_function()
  File "/usr/lib/python3.8/multiprocessing/util.py", line 357, in _exit_function
    p.join()
  File "/usr/lib/python3.8/multiprocessing/process.py", line 149, in join
    res = self._popen.wait(timeout)
  File "/usr/lib/python3.8/multiprocessing/popen_fork.py", line 47, in wait
    return self.poll(os.WNOHANG if timeout == 0.0 else 0)
  File "/usr/lib/python3.8/multiprocessing/popen_fork.py", line 27, in poll
    pid, sts = os.waitpid(self.pid, flag)
KeyboardInterrupt

好吧,看来这可能是我的用户错误。我将这个问题发布到推特上,其中用户@strinkleydimbu1 指出我可以使用守护程序标志产生预期的行为。具体你可以改变这一行:

sub_proc = mp.Process(target=spin_proc)

sub_proc = mp.Process(target=spin_proc, daemon=True)

并且程序运行符合预期。如果 parent 死亡,守护进程标志告诉 python 杀死所有 children。如果没有这个,它一定会无限期地等待 child 完成。令我惊讶的是,异常只会挂起而不是做任何事情,但由于我没有彻底阅读文档,我不能说这种行为是否真的出乎意料。