为了账号安全,请及时绑定邮箱和手机立即绑定

Cython,Python和KeyboardInterrupt被忽略

Cython,Python和KeyboardInterrupt被忽略

尚方宝剑之说 2021-03-12 15:11:37
有没有一种方法可以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的东西。
查看完整描述

2 回答

?
慕虎7371278

TA贡献1802条经验 获得超4个赞

您必须定期检查未决信号,例如,在模拟循环的第N次迭代中:


from cpython.exc cimport PyErr_CheckSignals


cdef Run(self):

    while True:

        # do some work

        PyErr_CheckSignals()

PyErr_CheckSignals将运行与信号模块一起安装的信号处理程序(KeyboardInterrupt必要时包括升高)。


PyErr_CheckSignals速度非常快,可以经常调用它。请注意,应该从主线程调用它,因为Python在主线程中运行信号处理程序。从辅助线程调用它无效。


解释


由于信号是在不可预测的时间异步传递的,因此直接从信号处理程序运行任何有意义的代码是有问题的。因此,Python将传入的信号排队。该队列稍后作为解释器循环的一部分进行处理。


如果您的代码已完全编译,则解释器循环将永远不会执行,并且Python没有机会检查和运行排队的信号处理程序。


查看完整回答
反对 回复 2021-03-27
?
婷婷同学_

TA贡献1844条经验 获得超8个赞

如果您尝试处理KeyboardInterrupt释放GIL的代码(例如,因为它使用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()


查看完整回答
反对 回复 2021-03-27
  • 2 回答
  • 0 关注
  • 238 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号