猿问

Cython,Python和KeyboardInterrupt被忽略

有没有一种方法可以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的东西。


尚方宝剑之说
浏览 239回答 2
2回答

慕虎7371278

您必须定期检查未决信号,例如,在模拟循环的第N次迭代中:from cpython.exc cimport PyErr_CheckSignalscdef Run(self):&nbsp; &nbsp; while True:&nbsp; &nbsp; &nbsp; &nbsp; # do some work&nbsp; &nbsp; &nbsp; &nbsp; PyErr_CheckSignals()PyErr_CheckSignals将运行与信号模块一起安装的信号处理程序(KeyboardInterrupt必要时包括升高)。PyErr_CheckSignals速度非常快,可以经常调用它。请注意,应该从主线程调用它,因为Python在主线程中运行信号处理程序。从辅助线程调用它无效。解释由于信号是在不可预测的时间异步传递的,因此直接从信号处理程序运行任何有意义的代码是有问题的。因此,Python将传入的信号排队。该队列稍后作为解释器循环的一部分进行处理。如果您的代码已完全编译,则解释器循环将永远不会执行,并且Python没有机会检查和运行排队的信号处理程序。

婷婷同学_

如果您尝试处理KeyboardInterrupt释放GIL的代码(例如,因为它使用cython.parallel.prange),则需要重新获取GIL才能调用PyErr_CheckSignals。以下代码段(改编自上面的@ nikita-nemkin的答案)说明了您需要执行的操作:from cpython.exc cimport PyErr_CheckSignalsfrom cython.parallel import prangecdef Run(self) nogil:&nbsp; &nbsp; with nogil:&nbsp; &nbsp; &nbsp; &nbsp; for i in prange(1000000)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # do some work but check for signals every once in a while&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if i % 10000 == 0:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; with gil:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; PyErr_CheckSignals()
随时随地看视频慕课网APP

相关分类

Python
我要回答