Реализация обработки сигналов в спящем цикле
Один из модулей в приложении, над которым я работаю, предназначен для использования в качестве длительного процесса в Linux, и я хотел бы, чтобы он корректно обрабатывал SIGTERM, SIGHUP и, возможно, другие сигналы. Основная часть программы на самом деле представляет собой цикл, который периодически запускает функцию (которая, в свою очередь, пробуждает другой поток, но это менее важно). Это выглядит примерно так:
while True:
try:
do_something()
sleep(60)
except KeyboardInterrupt:
break
cleanup_and_exit()
Теперь я хотел бы добавить SIGTERM и выйти из цикла, так же, как KeyboardInterrupt
исключение
Одна мысль, которую я имею, состоит в том, чтобы добавить флаг, который будет установлен в True с помощью функции обработчика сигнала, и заменить sleep(60) на sleep(0.1) или что-то еще со счетчиком, который считает секунды:
_exit_flag = False
while not _exit_flag:
try:
for _ in xrange(600):
if _exit_flag: break
do_something()
sleep(0.1)
except KeyboardInterrupt:
break
cleanup_and_exit()
и где-то еще:
def signal_handler(sig, frame):
_exit_flag = True
Но я не уверен, что это лучший / самый эффективный способ сделать это.
1 ответ
Вместо использования часового в главном цикле и, следовательно, необходимости просыпаться чаще, чем вы хотели бы проверить, почему бы не перенести очистку в обработчик? Что-то вроде:
class BlockingAction(object):
def __new__(cls, action):
if isinstance(action, BlockingAction):
return action
else:
new_action = super(BlockingAction, cls).__new__(cls)
new_action.action = action
new_action.active = False
return new_action
def __call__(self, *args, **kwargs):
self.active = True
result = self.action(*args, **kwargs)
self.active = False
return result
class SignalHandler(object):
def __new__(cls, sig, action):
if isinstance(action, SignalHandler):
handler = action
else:
handler = super(SignalHandler, cls).__new__(cls)
handler.action = action
handler.blocking_actions = []
signal.signal(sig, handler)
return handler
def __call__(self, signum, frame):
while any(a.active for a in self.blocking_actions):
time.sleep(.01)
return self.action()
def blocks_on(self, action):
blocking_action = BlockingAction(action)
self.blocking_actions.append(blocking_action)
return blocking_action
def handles(signal):
def get_handler(action):
return SignalHandler(signal, action)
return get_handler
@handles(signal.SIGTERM)
@handles(signal.SIGHUP)
@handles(signal.SIGINT)
def cleanup_and_exit():
# Note that this assumes that this method actually exits the program explicitly
# If it does not, you'll need some form of sentinel for the while loop
pass
@cleanup_and_exit.blocks_on
def do_something():
pass
while True:
do_something()
time.sleep(60)