SIGINT的默认处理程序引发KeyboardInterrupt。但是,如果程序在__del__方法内(由于正在进行的垃圾收集),则会忽略该异常,并将以下消息打印到stderr:
Exception KeyboardInterrupt in <...> ignored
因此,尽管接收到SIGINT,程序仍继续工作。当然,我可以为SIGINT定义自己的处理程序,将全局变量sigint_received设置为True,然后经常检查程序中变量的值。但这看起来很难看。
有没有一种优雅可靠的方法来确保python程序在收到SIGINT后被中断?
答案 0 :(得分:1)
在我深入研究解决方案之前,我想强调docs for object.__del__
(强调我的)中可怕的红色“警告:”侧边栏:
由于调用
__del__()
方法的不稳定情况,将忽略执行期间发生的异常,并向sys.stderr
打印警告。 [...]__del__()
方法应该保持维持外部不变量所需的绝对最小值。
这告诉我,任何被交互式用户__del__
打断的严重风险的Ctrl-C
方法可能会做得太多。因此,我的第一个建议是寻找最小化__del__
方法的方法,无论它是什么。
或者换句话说:如果你的__del__
方法确实 做了“所需的绝对最低限度”,那么如何在中途杀死这个过程是安全的?
我能找到的唯一解决方案确实是signal.SIGINT
sys.exit
...但很多明显的技巧都不起作用:
sys.exit
从信号处理程序调用{{1}}刚刚引发了SystemExit
异常,该异常被忽略。 Python的C API文档表明Python解释器不可能在__del__
方法期间引发任何异常:
void
custom signal handler(PyObject *obj)
[当......时调用]例如,当
PyErr_WriteUnraisable
方法中发生异常时,解释器无法实际引发异常[...]。
你在信号处理程序中设置一个全局“drop dead”变量的想法只能部分地起作用 - 尽管它更新了变量,但是在{{之后'没有任何机会读取该变量1}}方法返回。因此,在几秒钟内,__del__
似乎没有做任何事情。
如果您只想“最终”终止进程,这可能已经足够了,因为只要Ctrl-C
方法返回,将退出。但是,由于您可能希望在没有等待的情况下关闭进程(__del__
和SIGINT
通常来自不耐烦的用户),这样做不会。
KeyboardInterrupt
由于我找不到说服Python解释器自杀的方法,我的解决方案是让(更有说服力的)操作系统为我做。此信号处理程序使用__del__()
向其自己的进程ID发送更强的os.kill
,从而导致Python解释器本身退出。
SIGTERM
设置自定义信号处理程序后,def _sigterm_this_process(signum, frame):
pid = os.getpid()
os.kill(pid, signal.SIGTERM)
return
# Elsewhere...
signal.signal(signal.SIGINT, _sigterm_this_process)
导致Ctrl-C
方法(以及整个程序)立即退出。