为什么使用 threading.Event 会导致 SIGTERM 未被捕获?

发布于 2024-09-06 14:21:08 字数 900 浏览 8 评论 0原文

我有一个线程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 被传递(即,除了退出之外,“handled”永远不会被打印):

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 会杀死信号处理程序?

I have a threaded Python daemon. Like any good daemon, it wants to launch all of its worker threads, then wait around until it's told to terminate. The normal signal for termination is SIGTERM, and in most languages I'd hold to terminate by waiting on an event or mutex, so using threading.Event made sense to me. The problem is that Python's Event object and Unix signals don't appear to be playing well together.

This works as expected, terminating on 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"

but this results in no SIGTERM being delivered (i.e., quite apart from quitting, "handled" never gets printed):

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"

So my question is:

  1. Am I misusing threading.Event in some way?
  2. If I am not, is there an alternative other than the poll-and-sleep mechanism from the first example?
  3. Also if I am not, why does using threading.Event kill the signal handler?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一紙繁鸢 2024-09-13 14:21:08

来自 有关信号的 Python 文档

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

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

然而,在 signal 中,有一个 < code>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 上检查了它,它有效。如果您的应用程序不使用大量其他信号,我认为它不再归类为轮询和睡眠。

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

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

From Python documentation on signals:

Although Python signal handlers are called asynchronously as far as the Python user is concerned, they can only occur between the “atomic” instructions of the Python interpreter. This means that signals arriving during long calculations implemented purely in C (such as regular expression matches on large bodies of text) may be delayed for an arbitrary amount of time.

I tested various threading and thread classes and none of them work the way you want it -- this is probably because of how Python handles signals.

In signal, however, there is a pause() function that sleeps until a signal is received by the process. Your modified example would look like this:

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"

I checked it on Linux, it works. I don't think it classifies as poll-and-sleep anymore if your application doesn't use a lot of other signals.

It's good to handle SIGINT and SIGHUP too, to properly handle user interruption (usually by pressing Ctrl+C) and user disconnection (closing the parent terminal) respectively.

Also, note that signal.pause() is not available on Windows systems.

花落人断肠 2024-09-13 14:21:08

在 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)

In Python 3, this works:

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()

It looks that in Python 2.7 it works only if you specify wait interval: stop_event.wait(number_of_seconds)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文