当前位置: 首页 > 面试题库 >

多线程Python中的信号处理

孙经艺
2023-03-14
问题内容

这应该非常简单,并且令我感到惊讶的是,我还没找到关于stackoverflow的答案。

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

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

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

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

1)

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

2)

$ python main.py
ctrl+c

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

run started
handling signal: 15
run exited
join completed

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

run started

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

我在这里想念什么?


问题答案:

问题是,如Python信号处理程序的执行中所述:

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

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

您的主线程被阻塞在上threading.Thread.join,这最终意味着它在pthread_join调用中被C阻塞了。当然,这不是“长时间运行的计算”,而是syscall上的一个块……但是,在该调用结束之前,您的信号处理程序将无法运行。

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

所以你能对它做点啥?

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

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



 类似资料:
  • 在Linux中,当一个程序(可能有多个线程)收到信号(如SIGTERM或SIGHUP)时会发生什么? 哪个线程拦截信号?多个线程可以获得相同的信号吗?是否有专门处理信号的特殊线程?如果没有,那么处理信号的线程内部会发生什么?信号处理程序例程完成后,执行如何继续?

  • 问题内容: 在Linux中,当程序(可能具有多个线程)接收到诸如SIGTERM或SIGHUP之类的信号时会发生什么? 哪个线程拦截信号?多个线程可以得到相同的信号吗?有专门用于处理信号的特殊线程吗?如果不是,那么在处理信号的线程内部会发生什么?信号处理程序例程完成后,如何恢复执行? 问题答案: 根据您所使用的Linux内核版本,这会有些许细微差别。 假设有2.6个posix线程,并且如果您正在谈论

  • 问题内容: 我有2个线程(线程1和线程2)。而且我有信号处理。每当发生线程2时,都应处理该信号。为此,我写了下面的程序 我编译并运行该程序。每1秒打印一次“ thread1 active”,每3秒打印一次“ thread2 active”。 现在我生成了。但是它会像上面那样显示“ thread1 active”和“ thread2 active”消息。再次生成了,现在每3秒仅打印一次“ threa

  • 问题内容: 我试图理解多处理比线程的优势。我知道多处理绕过了全局解释器锁,但是还有什么其他优点,线程不能做同样的事情? 问题答案: 该模块使用线程,该模块使用进程。不同之处在于线程在相同的内存空间中运行,而进程具有单独的内存。这使得在具有多处理的进程之间共享对象更加困难。由于线程使用相同的内存,因此必须采取预防措施,否则两个线程将同时写入同一内​​存。这就是全局解释器锁的作用。 生成过程比生成线程

  • 我有一个图像路径列表,我想在进程或线程之间划分,以便每个进程处理列表的某些部分。处理包括从磁盘加载图像,进行一些计算并返回结果。我正在使用Python 2.7 下面是我如何创建辅助进程 我所面临的问题是,当我在initializer函数中记录初始化时间时,我知道worker不是并行初始化的,而是每个worker都以5秒的间隔初始化,下面是供参考的日志 我尝试过使用将同时启动辅助线程 我知道Wind

  • 问题内容: 我发现在Python 3.4中,用于多处理/线程的库很少:多处理vs线程与asyncio。 但是我不知道使用哪个,或者是“推荐的”。他们做的是同一件事还是不同?如果是这样,则将哪一个用于什么?我想编写一个在计算机上使用多核的程序。但是我不知道我应该学习哪个图书馆。 问题答案: 它们旨在(略有)不同的目的和/或要求。CPython(典型的主线Python实现)仍然具有全局解释器锁,因此多