报警信号未在无限循环中触发



如果函数运行超过3秒(例如),我将尝试使其超时。我用的是信号和警报器,但警报器从不响。我想要一个适用于任何函数的超时机制。作为我面临的问题的一个例子:

import signal
def foobar():
    x = 42
    while x >= 20:
        if x >= 40:
            x = 23
    return x
def handle_alarm(*args):
    print("Alarm raised")
    raise TimeoutException("timeout reached")
signal.signal(signal.SIGALRM, handle_alarm)
signal.alarm(3)
try:
    print(foobar())
except:
    print("Exception Caught")

当运行时,我的程序永远运行,而我的处理程序永远不会运行。知道为什么会这样吗?

顺便说一句,如果我从foobar中删除if语句,那么警报就会触发。

在我的系统MacOSX和MacPorts上,我用许多版本的Python测试了您的代码。唯一显示您发现的"bug"的版本是2.7。超时适用于2.4、2.5、2.6、3.3和3.4。

现在,为什么会发生这种情况,我们能做些什么呢?

我认为发生这种情况是因为foobar()是一个紧密的循环,它从不将控制权"让出"给Python的主循环。它只是以最快的速度运行,没有做任何有用的工作,但阻止了Python处理信号。

这将有助于理解*nix中的信号通常是如何处理的。由于很少有库函数是"异步信号安全的",因此在C信号处理程序中无法直接执行太多操作。Python需要调用用Python编写的信号处理程序,但它不能直接在使用C注册的信号处理器中调用。因此,程序在信号处理程序中所做的一件典型的事情是设置一些标志来指示信号已被接收,然后返回。然后,在主循环中,检查该标志(直接或通过使用"管道",可以在信号处理程序中写入该"管道"并打开poll()-ed或select()-ed)。

因此,我假设Python主循环正在愉快地执行您的foobar()函数,并且有一个信号进入,它设置一些内部状态以知道它需要处理该信号,然后它等待foobar()结束,否则,至少foobar()调用一些可中断的函数,如sleep()print()

事实上,如果在foobar()的循环中添加sleep(任意时间)或print语句,您将在Python2.7(以及其他版本)中获得所需的超时。

总之,在繁忙的循环中睡个短觉是个好主意,可以"放松"他们,从而帮助安排其他可能需要做的工作。你也不必在每次迭代中都睡觉——在这种情况下,只要在循环中每1000次睡一次就可以了。

最新更新