多线程Python中的信号处理 [英] Signal handling in multi-threaded Python

查看:353
本文介绍了多线程Python中的信号处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这应该非常简单,并且令我感到惊讶的是,我还没找到在stackoverflow上已经回答过的问题.

This should be very simple and I'm very surprised that I haven't been able to find this questions answered already on stackoverflow.

我有一个类似守护程序的程序,该程序需要响应SIGTERM和SIGINT信号才能与新贵一起正常工作.我读到最好的方法是在与主线程不同的线程中运行程序的主循环,并让主线程处理信号.然后,当接收到信号时,信号处理程序应通过设置通常在主循环中检查的哨兵标志来告诉主循环退出.

I have a daemon like program that needs to respond to the SIGTERM and SIGINT signals in order to work well with upstart. I read that the best way to do this is to run the main loop of the program in a separate thread from the main thread and let the main thread handle the signals. Then when a signal is received the signal handler should tell the main loop to exit by setting a sentinel flag that is routinely being checked in the main loop.

我已经尝试过这样做,但是它没有按我预期的方式工作.请参见下面的代码:

I've tried doing this but it is not working the way I expected. See the code below:

from threading import Thread
import signal
import time
import sys

stop_requested = False    

def sig_handler(signum, frame):
    sys.stdout.write("handling signal: %s\n" % signum)
    sys.stdout.flush()

    global stop_requested
    stop_requested = True    

def run():
    sys.stdout.write("run started\n")
    sys.stdout.flush()
    while not stop_requested:
        time.sleep(2)

    sys.stdout.write("run exited\n")
    sys.stdout.flush()

signal.signal(signal.SIGTERM, sig_handler)
signal.signal(signal.SIGINT, sig_handler)

t = Thread(target=run)
t.start()
t.join()
sys.stdout.write("join completed\n")
sys.stdout.flush()

我通过以下两种方式对此进行了测试:

I tested this in the following two ways:

1)

$ python main.py > output.txt&
[2] 3204
$ kill -15 3204

2)

$ python main.py
ctrl+c

在两种情况下,我都希望将其写入输出:

In both cases I expect this written to the output:

run started
handling signal: 15
run exited
join completed

在第一种情况下,程序退出了,但是我所看到的是:

In the first case the program exits but all I see is:

run started

在第二种情况下,当按ctrl + c键且程序不退出时,似乎忽略了SIGTERM信号.

In the second case the SIGTERM signal is seemingly ignored when ctrl+c is pressed and the program doesn't exit.

我在这里想念什么?

推荐答案

问题是,正如

Python信号处理程序不会在低级(C)信号处理程序内执行.相反,低级信号处理程序设置一个标志,该标志告诉虚拟机在以后的位置(例如,在下一条字节码指令处)执行相应的Python信号处理程序.

A Python signal handler does not get executed inside the low-level (C) signal handler. Instead, the low-level signal handler sets a flag which tells the virtual machine to execute the corresponding Python signal handler at a later point(for example at the next bytecode instruction)

...

纯粹用C语言实现的长时间运行的计算(例如,在大文本正文上进行正则表达式匹配)可能会在任意时间内无中断运行,而不管收到任何信号.计算完成后,将调用Python信号处理程序.

A long-running calculation implemented purely in C (such as regular expression matching on a large body of text) may run uninterrupted for an arbitrary amount of time, regardless of any signals received. The Python signal handlers will be called when the calculation finishes.

您的主线程在threading.Thread.join上被阻塞,这最终意味着在pthread_join调用中它在C中被阻塞.当然,这不是一个``长时间运行的计算'',而是syscall上的一个块...但是,尽管如此,直到该调用完成,您的信号处理程序才能运行.

Your main thread is blocked on threading.Thread.join, which ultimately means it's blocked in C on a pthread_join call. Of course that's not a "long-running calculation", it's a block on a syscall… but nevertheless, until that call finishes, your signal handler can't run.

而且,尽管在某些平台上,pthread_join会因信号上的EINTR而失败,但在其他平台上却不会.在Linux上,我相信这取决于您选择的是BSD样式还是默认的siginterrupt行为,但是默认值为no.

And, while on some platforms pthread_join will fail with EINTR on a signal, on others it won't. On linux, I believe it depends on whether you select BSD-style or default siginterrupt behavior, but the default is no.

那么,你能做什么呢?

好吧,我很确定在Python 3.3中对信号处理的更改实际上已更改 Linux上的默认行为,因此升级后无需执行任何操作;只需在3.3+下运行,您的代码即可按预期工作.至少它对我来说适用于OS X上的CPython 3.4和Linux上的3.3. (如果对此我错了,我不确定它是否是CPython中的错误,因此您可能想在python-list上引发它,而不是打开一个问题……)

Well, I'm pretty sure the changes to signal handling in Python 3.3 actually changed the default behavior on Linux so you won't need to do anything if you upgrade; just run under 3.3+ and your code will work as you're expecting. At least it does for me with CPython 3.4 on OS X and 3.3 on Linux. (If I'm wrong about this, I'm not sure whether it's a bug in CPython or not, so you may want to raise it on python-list rather than opening an issue…)

另一方面,在3.3之前的版本中,signal模块绝对不会提供您自己解决此问题所需的工具.因此,如果您不能升级到3.3,解决方案是等待可中断的内容,例如ConditionEvent.子线程在退出之前立即通知事件,而主线程在加入子线程之前等待事件.这绝对是hacky.而且我找不到任何能保证会有所作为的东西.它恰好适用于OS X上的各种CPython 2.7和3.2以及Linux上的2.6和2.7……

On the other hand, pre-3.3, the signal module definitely doesn't expose the tools you'd need to fix this problem yourself. So, if you can't upgrade to 3.3, the solution is to wait on something interruptible, like a Condition or an Event. The child thread notifies the event right before it quits, and the main thread waits on the event before it joins the child thread. This is definitely hacky. And I can't find anything that guarantees it will make a difference; it just happens to work for me in various builds of CPython 2.7 and 3.2 on OS X and 2.6 and 2.7 on Linux…

这篇关于多线程Python中的信号处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆