在嵌套的 multiprocessing.Process 上调用 start 后,Python 不会引发异常

发布于 2025-01-09 19:30:06 字数 2678 浏览 0 评论 0原文

我在程序中遇到了一个问题,即启动嵌套子进程后不会引发异常。也就是说,我有一个在 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

I have run in to an issue in my program where exceptions are not thrown after launching a nested subprocess. That is, I have a method that I run in a multiprocessing.Process. Inside of that process ("the first process"), I launch another multiprocessing.Process. After calling start on the second process, the first process continues but does not raise exceptions, instead hanging when an exception is raised.

I cannot tell if this is expected behavior or I have stumbled across a bug in Python. Here is a minimal example to demonstrate the issue:

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()

The output is:

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

And it will continue printing "spin" indefinitely until I hit Ctrl-C. I would have expected the exception to get raised and crash the program.

I have confirmed this behavior in Python 3.8.10 and Python 3.9.2.

Is this expected behavior? If so, why? It has the effect of hiding exceptions in my code and unexpectedly blocking, which was very frustrating when trying to debug something new and not seeing any output. If it appears to be a bug, please let me know and I will report it to the Python bug tracker.

Thank you! And here is the output when pressing Ctrl-C. It seems it wanted to throw the Exception, but was hanging for some reason:

^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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

浅忆 2025-01-16 19:30:06

好吧,看来这可能是我的用户错误。我将这个问题发布到 Twitter,其中用户 @strinkleydimbu1 指出我可以使用守护进程标志产生预期的行为。具体来说,您可以更改此行:

sub_proc = mp.Process(target=spin_proc)

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

程序将按预期运行。守护进程标志告诉 python 如果父进程死亡则杀死所有子进程。如果没有这个,它一定会无限期地等待孩子完成。令我惊讶的是,异常只会挂起而不执行任何操作,但由于我没有彻底阅读文档,所以我不能说这种行为是否确实是意外的。

Okay it seems this was probably user error on my part. I posted this question to twitter where user @strinkleydimbu1 pointed out that I could produce the expected behavior with the daemon flag. Specifically you can change this line:

sub_proc = mp.Process(target=spin_proc)

to

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

And the program behaves as expected. The daemon flag tells python to kill all children if the parent dies. Without this, it must have been waiting indefinitely for the child to finish. It was surprising to me that exceptions would just hang instead of doing anything, but since I have not thoroughly read the docs I can't say if the behavior is truly unexpected.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文