【问题标题】:Why does using threading.Event result in SIGTERM not being caught?为什么使用 threading.Event 会导致 SIGTERM 未被捕获?
【发布时间】:2010-06-23 13:44:56
【问题描述】:

我有一个线程化的 Python 守护进程。像任何好的守护进程一样,它想要启动它的所有工作线程,然后等待直到它被告知终止。终止的正常信号是SIGTERM,在大多数语言中,我会通过等待事件或互斥体来终止,所以使用threading.Event 对我来说是有意义的。问题是 Python 的 Event 对象和 Unix 信号似乎不能很好地结合在一起。

这按预期工作,终止于SIGTERM

import signal
import time

RUN = True

def handle(a, b):
    global RUN
    print "handled"
    RUN = False

signal.signal(signal.SIGTERM, handle)
while RUN:
    time.sleep(0.250)
print "Stopping"

但这导致没有SIGTERM 被传递(即,除了退出之外,“已处理”永远不会被打印):

import signal
import threading

RUN_EVENT = threading.Event()

def handle(a, b):
    print "handled"
    RUN_EVENT.set()

signal.signal(signal.SIGTERM, handle)
RUN_EVENT.wait()
print "Stopping"

所以我的问题是:

  1. 我是否以某种方式滥用了threading.Event
  2. 如果不是,除了第一个示例中的轮询和睡眠机制之外,还有其他替代方法吗?
  3. 另外,如果我不是,为什么使用threading.Event 会杀死信号处理程序?

【问题讨论】:

    标签: python events multithreading signals daemon


    【解决方案1】:

    来自Python documentation on signals

    尽管就 Python 用户而言,Python 信号处理程序是异步调用的,但它们只能出现在 Python 解释器的“原子”指令之间。这意味着在纯 C 中实现的长时间计算期间到达的信号(例如在大量文本上的正则表达式匹配)可能会延迟任意时间。

    我测试了各种 threadingthread 类,但它们都没有按照你想要的方式工作——这可能是因为 Python 处理信号的方式。

    然而,在signal 中,有一个pause() 函数在进程接收到信号之前一直处于休眠状态。您修改后的示例如下所示:

    import signal
    
    RUN = True
    
    def handle(a, b):
        global RUN
        print "handled"
        RUN = False
    
    signal.signal(signal.SIGTERM, handle)
    signal.signal(signal.SIGINT, handle)
    signal.signal(signal.SIGHUP, handle)
    
    while RUN:
        signal.pause()
    
    print "Stopping"
    

    我在 Linux 上检查过,它可以工作。如果您的应用程序不使用很多其他信号,我认为它不再归类为 poll-and-sleep。

    处理SIGINTSIGHUP也很好,分别正确处理用户中断(通常通过按Ctrl+C)和用户断开连接(关闭父终端)。

    另外,请注意signal.pause() 在 Windows 系统上不可用

    【讨论】:

    • Python 的烦人特性,但完美的解决方案。谢谢你。我承认我没有想到要寻找对signal 使用的额外限制,因为我知道等效的 C 可以正常工作。
    • 这是否意味着处理信号的唯一方法是将主线程(父线程)专用于捕获信号(通过阻塞'signal.pause()')?这意味着主线程不能再做任何有用的事情了。换句话说,你不能有一个主/从模型(两个线程相互通信),但你需要一个主/从+工作者模型(两个工作者相互通信,主不做任何事情。跨度>
    【解决方案2】:

    在 Python 3 中,这是可行的:

    from threading import Event
    from os import getpid
    from signal import SIGTERM, SIGINT, SIGHUP, signal
    
    stop_event = Event()
    
    def handler(signum, frame):
        stop_event.set()
    
    def main():
        signal(SIGTERM, handler)
        signal(SIGINT, handler)
        signal(SIGHUP, handler)
        print('start, pid:', getpid())
        stop_event.wait()
        print('done')
    
    if __name__ == '__main__':
        main()
    

    看起来在 Python 2.7 中它只有在您指定等待间隔时才有效:stop_event.wait(number_of_seconds)

    【讨论】:

      猜你喜欢
      • 2020-12-11
      • 2016-04-03
      • 1970-01-01
      • 2011-02-09
      • 1970-01-01
      • 2010-12-02
      • 2020-05-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多