简体   繁体   English

在睡眠循环上实现信号处理

[英]Implementing signal handling on a sleep loop

One of the modules in an app I'm working on is intended to be used as a long-running process on Linux, and I would like it to gracefully handle SIGTERM, SIGHUP and possibly other signals. 我正在开发的应用程序中的一个模块旨在用作Linux上长时间运行的进程,我希望它能够优雅地处理SIGTERM,SIGHUP和其他信号。 The core part of the program is in fact a loop which periodically runs a function (which in turn wakes up another thread, but this is less important). 该程序的核心部分实际上是一个循环,该循环定期运行一个函数(依次唤醒另一个线程,但这并不重要)。 It looks more or less like this: 看起来或多或少是这样的:

while True: 
    try:
        do_something()
        sleep(60)
    except KeyboardInterrupt:
        break

cleanup_and_exit()

What I'd like to add now is to catch SIGTERM and exit the loop, the same way a KeyboardInterrupt exception would. 我现在要添加的是捕获SIGTERM并退出循环,就像KeyboardInterrupt异常一样。

One thought I have is to add a flag which will be set to True by the signal handler function, and replace the sleep(60) with sleep(0.1) or whatever, with a counter that counts seconds: 我想到的一个想法是添加一个将由信号处理程序函数设置为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()

and somewhere else: 还有其他地方:

def signal_handler(sig, frame): 
    _exit_flag = True

But I'm not sure this is the best / most efficient way to do it. 但是我不确定这是最好/最有效的方法。

Rather than using a sentinel in the main loop and thus having to wake more frequently than you'd really like to check for it, why not push the cleanup in to the handler? 与其在主循环中使用哨兵,而不必比您真正想要检查的次数更频繁地唤醒,为什么不将清理推入处理程序? Something like: 就像是:

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM