2014-10-05 81 views
2

我试图超时运行超过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语句,那么闹钟会触发。

+0

可能是一个python的bug。这在pypy下工作。我认为你应该在错误跟踪器上提交这个文件。 – simonzack 2014-10-05 05:22:20

+0

你在使用什么操作系统?视窗? – Ewan 2014-10-05 06:23:10

+0

@Ewan'SIGALRM'只存在于linux上。 – simonzack 2014-10-05 06:45:28

回答

2

在我的系统中,Mac OS X带有MacPorts,我用很多版本的Python测试了你的代码。展示“bug”的唯一版本是2.7。超时在2.4,2.5,2.6,3.3和3.4中工作。

现在,为什么会发生这种情况,并且可以做些什么呢?

我认为这是因为你的foobar()是一个严密的循环,永远不会“控制”回到Python的主循环。它只是运行得尽可能快,没有任何有用的工作,但阻止了Python处理信号。

这将有助于了解* nix中的信号通常如何处理。由于少数库函数是“异步信号安全”,因此在C信号处理程序中不能直接完成很多工作。 Python需要调用用Python编写的信号处理程序,但它不能直接在使用C注册的信号处理程序中执行该操作。因此,程序在其信号处理程序中执行的典型操作是设置一些标志来指示信号已经收到,然后返回。然后在主循环中检查该标志(直接或通过使用可在信号处理程序中写入的“管道”和select()编辑)。

所以我会假设Python主循环正在愉快地执行你的foobar()函数,并且有一个信号进来,它设置了一些内部状态以知道它需要处理该信号,然后等待foobar()结束,或者失败,至少对于foobar()来调用一些可中断功能,如sleep()print()。实际上,如果您添加了睡眠(任意时间)或print声明到foobar()的循环,您将在Python 2.7(以及其他版本)中获得所需的超时。

无论如何,在忙碌的环路中进行短暂的睡眠总是一个好主意,以便“放松”它们,从而帮助安排可能需要做的其他工作。你不必在每次迭代时都睡觉 - 在这种情况下,通过循环每1000次只需要一个微小的睡眠就可以正常工作。