首页 > 解决方案 > Python 脚本中有多个 SIGTERM 处理程序 - 谁应该调用 sys.exit()?

问题描述

假设我有以下脚本:

import signal
import sys

class Klass:
   def __init__(self, name):
     self.name = name

     def disconnect_gracefully(*args):
         print(self.name)
         sys.exit()

     signal.signal(signal.SIGINT, disconnect_gracefully)


a = Klass("A")
b = Klass("B")

while True:
    pass

请注意,这两个类在 SIGINT 之后都有一个优雅的退出。当我运行它并 crtl-c 时,只打印“B”。

一般来说,在这样的情况下,应该打电话给什么sys.exit()- 两种情况都应该,都不应该?

标签: pythonoopsignals

解决方案


信号的 Python 文档仅说明了多次为同一信号设置处理程序时会发生什么:

特定信号的处理程序一旦设置,将保持安装状态,直到显式重置

[调用时signal.signal] 将返回上一个信号处理程序

这似乎意味着,就像 POSIX 信号一样,一个进程一次只能有一个处理给定信号的处理程序。

我们可以在您的程序中添加一些语句来显示signal.signal返回的内容(在 CPython 中):

import signal
import sys
class K:
  def __init__(self, name):
    self.name=name
    def disconnect(*args):
      print(self.name)
      sys.exit()
    self.disconnectfuncid=id(disconnect)
    self.oldsig=signal.signal(signal.SIGINT, disconnect)


>>> a=K("a")
>>> hex(a.disconnectfuncid)
'0x7fc5c1057f28'
>>> a.oldsig
<built-in function default_int_handler>
>>> signal.getsignal(signal.SIGINT)
<function K.__init__.<locals>.disconnect at 0x7fc5c1057f28>


>>> b=K("b")
>>> hex(b.disconnectfuncid)
'0x7fc5c0ed0950'
>>> b.oldsig
<function K.__init__.<locals>.disconnect at 0x7fc5c1057f28>
>>> signal.getsignal(signal.SIGINT)
<function K.__init__.<locals>.disconnect at 0x7fc5c0ed0950>


>>> while True:
...   pass
...^C b

所以处理程序从 开始<built-in function default_int_handler>,然后设置为实例中的函数,然后设置为实例中的disconnect函数,后者是传递信号时调用的函数。adisconnectb

如果您想在收到 SIGINT 时退出,并在退出前运行多个函数,一种方法是调用以下命令(这将禁止打印回溯和KeyboardInterrupt消息):

 signal.signal(signal.SIGINT, lambda *args: sys.exit())

然后使用atexit.register注册每个函数。


推荐阅读