2014-10-05 4 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") 

При запуске моя программа запускается вечно, и мой обработчик никогда не запускается. Любая идея, почему это так?

Как в стороне, если я удаляю оператор if из foobar, то будильник срабатывает.

+0

Возможно, ошибка python. Это работает под pypy. Я думаю, вы должны записать это на трекер ошибок. – simonzack

+0

В какой ОС вы работаете? Окна? – Ewan

+0

@Ewan 'SIGALRM' существует только на linux. – simonzack

ответ

2

В моей системе Mac OS X с MacPorts я проверил ваш код со многими версиями Python. Единственная версия, в которой отображается найденная вами ошибка, равна 2,7. Таймаут работает в 2.4, 2.5, 2.6, 3.3 и 3.4.

Теперь, почему это происходит, и что можно сделать по этому поводу?

Я думаю, что это происходит потому, что ваш foobar() - это замкнутая петля, которая никогда не «возвращает» управление в основной цикл Python. Он работает как можно быстрее, не делая никакой полезной работы, но не позволяя Python обрабатывать сигнал.

Это поможет понять, как обычно обрабатываются сигналы в * nix. Поскольку несколько функций библиотеки являются «безопасными для асинхронного сигнала», мало что можно сделать в обработчике сигналов C напрямую. Python должен вызывать ваш обработчик сигнала, который написан на Python, но он не может сделать это непосредственно в обработчике сигнала, который он регистрирует с использованием C. Так что типичная вещь, которую программы выполняют в своих обработчиках сигналов, - это установить некоторый флаг, чтобы указать, что сигнал был получен, а затем возвращен. В основном цикле, этот флаг проверяется (либо напрямую, либо с помощью «трубы», который может быть записан в обработчик сигнала, и poll() ed или select() ed on).

Так что я бы предположить, что основной цикл Python, к счастью выполнения вашей foobar() функции, и сигнал приходит, он устанавливает некоторое внутреннее состояние, чтобы знать, что необходимо обрабатывать этот сигнал, а затем его ждет на foobar() до конца, или сбой, по крайней мере, для foobar() для вызова некоторой прерывистой функции, такой как sleep() или print().

И действительно, если вы добавляете либо сон (за любое количество времени), либо оператор print в цикл foobar(), вы получите таймаут, который вы желаете в Python 2.7 (а также в других версиях).

В общем-то, неплохо по-прежнему коротко спать в петлях занятости, чтобы «расслабить» их, тем самым помогая планировать другую работу, которая может потребоваться. Вам также не нужно спать на каждой итерации - только крошечный сон каждые 1000 раз через цикл будет работать нормально в этом случае.

Смежные вопросы