Cython、Python和键盘中断被忽略

2024-10-01 07:14:06 发布

您现在位置:Python中文网/ 问答频道 /正文

有没有一种方法可以基于嵌入在Cython扩展中的循环来中断(Ctrl+C)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信号从扩展内部处理异常:

^{pr2}$

然后:

signal(SIGABRT, handler);
signal(SIGFPE,  handler);
signal(SIGILL,  handler);
signal(SIGINT,  handler);
signal(SIGSEGV, handler);
signal(SIGTERM, handler);

我不能用Python或者至少用Cython来实现吗?由于我即将在Windows/MinGW下移植我的扩展,所以我希望有一些不太适合Linux的东西。在


Tags: to方法run脚本signalmaindefsim
3条回答

当Cython运行与Python不接口的部分时,释放GIL,在主线程中运行loop(休眠或检查模拟状态),并在exceptclose中调用sim.Stop()(它可以设置一些标志,仿真可以定期检查)。在

如果您试图在释放GIL的代码中处理KeyboardInterrupt(例如,因为它使用cython.parallel.prange),则需要重新获取GIL来调用PyErr_CheckSignals。下面的片段(改编自@nikita nemkin的回答)说明了您需要做什么:

from cpython.exc cimport PyErr_CheckSignals
from cython.parallel import prange

cdef Run(self) nogil:
    with nogil:
        for i in prange(1000000)
            # do some work but check for signals every once in a while
            if i % 10000 == 0:
                with gil:
                    PyErr_CheckSignals()

您必须定期检查挂起的信号,例如,在模拟循环的每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就没有机会检查和运行排队的信号处理程序。在

相关问题 更多 >