有没有办法中断 (Ctrl+C
) 基于嵌入在 Cython 扩展中的循环的 Python 脚本?
我有以下 python 脚本:
def main():
# Intantiate simulator
sim = PySimulator()
sim.Run()
if __name__ == "__main__":
# Try to deal with Ctrl+C to abort the running simulation in terminal
# (Doesn't work...)
try:
sys.exit(main())
except (KeyboardInterrupt, SystemExit):
print '\n! Received keyboard interrupt, quitting threads.\n'
这会运行一个循环,该循环是 C++ Cython 扩展的一部分。
然后,在按下 Ctrl+C
的同时,KeyboardInterrupt
被抛出但被忽略,程序继续运行直到模拟结束。
我发现的解决方法是通过捕获 SIGINT
信号来处理扩展中的异常:
#include <execinfo.h>
#include <signal.h>
static void handler(int sig)
{
// Catch exceptions
switch(sig)
{
case SIGABRT:
fputs("Caught SIGABRT: usually caused by an abort() or assert()\n", stderr);
break;
case SIGFPE:
fputs("Caught SIGFPE: arithmetic exception, such as divide by zero\n",
stderr);
break;
case SIGILL:
fputs("Caught SIGILL: illegal instruction\n", stderr);
break;
case SIGINT:
fputs("Caught SIGINT: interactive attention signal, probably a ctrl+c\n",
stderr);
break;
case SIGSEGV:
fputs("Caught SIGSEGV: segfault\n", stderr);
break;
case SIGTERM:
default:
fputs("Caught SIGTERM: a termination request was sent to the program\n",
stderr);
break;
}
exit(sig);
}
然后:
signal(SIGABRT, handler);
signal(SIGFPE, handler);
signal(SIGILL, handler);
signal(SIGINT, handler);
signal(SIGSEGV, handler);
signal(SIGTERM, handler);
我不能用 Python 或至少用 Cython 来完成这项工作吗?当我即将在 Windows/MinGW 下移植我的扩展时,我希望能有一些不那么特定于 Linux 的东西。
最佳答案
您必须定期检查未决信号,例如,在模拟循环的每 N 次迭代中:
from cpython.exc cimport PyErr_CheckSignals
cdef Run(self):
while True:
# do some work
PyErr_CheckSignals()
PyErr_CheckSignals
将运行随 signal 安装的信号处理程序模块(这包括在必要时引发 KeyboardInterrupt
)。
PyErr_CheckSignals
相当快,经常调用就可以了。请注意,它应该从主线程调用,因为 Python 在主线程中运行信号处理程序。从工作线程调用它没有任何效果。
解释
由于信号是在不可预测的时间异步传递的,因此直接从信号处理程序运行任何有意义的代码都是有问题的。因此,Python 对传入信号进行排队。该队列稍后作为解释器循环的一部分进行处理。
如果您的代码已完全编译,则永远不会执行解释器循环,Python 也没有机会检查和运行排队的信号处理程序。
关于python - 忽略 Cython、Python 和 KeyboardInterrupt,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16769870/