我有以下test_mpi.py
python脚本:
from mpi4py import MPI
import time
class Foo:
def __init__(self):
print('Creation object.')
def __del__(self):
print('Object destruction.')
foo = Foo()
time.sleep(10)
如果我在没有求助于 mpiexec 的情况下执行它,使用一个简单的python test_mpi.py
,在 5 秒后按 CTRL+C,我得到以下输出:
ngreiner@Nathans-MacBook-Pro:~/Documents/scratch$ python test_mpi.py
Creation object.
^CTraceback (most recent call last):
File "test_mpi.py", line 26, in <module>
time.sleep(10)
KeyboardInterrupt
Object destruction.
ngreiner@Nathans-MacBook-Pro:~/Documents/scratch$
如果我使用mpiexec -np 1 python test_mpi.py
将其嵌入到 mpiexec 执行中,在 5 秒后再次按 CTRL+C,我现在得到:
ngreiner@Nathans-MacBook-Pro:~/Documents/scratch$ mpiexec -np 1 python test_mpi.py
Creation object.
^Cngreiner@Nathans-MacBook-Pro:~/Documents/scratch$
来自 python 的回溯和 __del__ 方法的执行已经消失。 对我来说,主要问题是不执行__del__方法,这应该在我的实际应用程序中进行一些清理。
知道从 mpiexec 启动 Python 执行时我如何执行 __del__ 方法吗?
提前非常感谢您的帮助,
(我的系统配置:macOS High Sierra 10.13.6,Python 3.7.4,open-mpi 4.0.1,mpi4py 3.0.2。
经过一番搜索,我找到了一个解决方案,可以在mpiexec
期间点击^C时恢复回溯的打印和__del__方法的执行。
在正常的python执行过程中(不是由mpiexec启动,直接从终端启动(,点击^C会向python发送SIGINT信号,将其转换为KeyboardInterrupt异常(https://docs.python.org/3.7/library/signal.html(。
但是,当在mpiexec
执行期间点击^C时,接收SIGINT信号的是mpiexec
进程,而不是将其传播到其子进程(例如python(,而是向其子进程发送SIGTERM信号(https://www.open-mpi.org/doc/current/man1/mpirun.1.php(。
因此,python似乎对SIGINT和SIGTERM信号的反应并不相似。
我发现的解决方法是使用信号模块,并为SIGTERM信号使用特定的处理程序,它只是引发键盘中断。这可以通过以下几行实现:
def sigterm_handler():
raise KeyboardInterrupt
import signal
signal.signal(signal.SIGTERM, sigterm_handler)
前者可以包含在执行的python脚本的顶部,或者,为了在每次python与mpiexec
和mpi4py包一起使用时保留这种行为,在mpi4py包的__init__.py文件的顶部。
此策略可能会产生副作用(我不知道(,应自行承担使用风险。
根据文档,不保证会调用del。所以你很幸运,它在非 mpi 程序上被调用。
对于简单情况,您可以使用 try/finally 来确保执行 finally 部分。 或者,更一般地说,使用上下文管理器
以下是此处很重要的文档引用:
不能保证对解释器退出时仍然存在的对象调用del(( 方法。
ngreiner 的回答对我有帮助,但至少对于 Python 2.7 和所有 Python 3 版本,处理程序函数需要两个参数。这个带有虚拟参数的修改代码片段对我有用:
import signal
def sigterm_handler(signum, frame):
raise KeyboardInterrupt
signal.signal(signal.SIGTERM, sigterm_handler)