【问题标题】:Python SIGINT SIGTERM does not exit instantly a while loopPython SIGINT SIGTERM 不会立即退出 while 循环
【发布时间】:2018-12-21 23:20:59
【问题描述】:
def do_stuff_before_python_terminates():
    save_variables_in_mysql()
    do_this_and_that()...

def main():
    do stuff
    while loops ect...

def sigterm(x, y):
    raise Exception()
def sigint(signal, frame):
    raise Exception()





signal.signal(signal.SIGINT, sigint)
signal.signal(signal.SIGTERM, sigterm)  
try:
    while True:
        main()
except Exception as e:
    logger.error("Exception")
    do_stuff_before_python_terminates()
    logger.log("sys.exit")
    sys.exit(0)

我在 Docker 容器中使用 Python

当我通过终端 tty 执行 ctrl+c 或停止图像时 python确实停止并且并不总是成功 “do_stuff_before_python_terminates()”。

原因是,当 python 随机处于 while 循环中时,我没有运气,它不会退出,它会停留在 while 循环中并继续执行其他操作,并且不会成功终止。

Docker 在杀死容器之前只等待 10 秒,然后瞧,它没有“do_stuff_before_python_terminates()”

我在这里做错了什么,如何解决即使在while循环中它也会立即退出并且“do_stuff_before_python_terminates()”的问题

更新说明:

如果python抛出错误

确实成功跳转到

except Exception as e:
    logger.error("Exception")
    do_stuff_before_python_terminates()
    logger.log("sys.exit")
    sys.exit(0)

如果我停止容器或使用 ctrl+c 并且 python 不在 while 循环中

确实成功跳转到

sigterm() or sigint() -> then raise an Exception() ->  then jumps to
except Exception as e:
    logger.error("Exception")
    do_stuff_before_python_terminates()
    logger.log("sys.exit")
    sys.exit(0)

如果我停止容器或使用 ctrl+c 并且 python 在循环中

it does stay in the loop 
do stuff
do stuff
do stuff
after nearly 20-30seconds
sigterm() or sigint() -> then raise an Exception() ->  then jumps to
except Exception as e:
    logger.error("Exception")
    do_stuff_before_python_terminates()
    logger.log("sys.exit")
    sys.exit(0)

我需要的是,当我停止容器或执行 ctrl+c (sigterm + sigint) 时,它会立即跳出 while 循环

sigterm() or sigint() -> then raise an Exception() ->  then jumps to
except Exception as e:
    logger.error("Exception")
    do_stuff_before_python_terminates()
    logger.log("sys.exit")
    sys.exit(0)

Docker确实会在10秒后杀死容器,所以python只有10秒的时间退出,效果是当python处于循环中时它永远不会do_stuff_before_python_terminates()

【问题讨论】:

  • 两种可能的解释:从main() 调用的代码安装了自己的信号处理程序,或者从main 调用的代码捕获了Exception
  • 对不起,不知道你的意思,我将结构更正为更具可读性,也许这就是它看起来像信号在主要功能中的原因,你能提供一个解决方案
  • 我的意思是,某些被调用的函数可能会干扰您所依赖的异常引发和捕获机制。也许不是这样,我不知道,但是您可以通过最小化代码来测试并排除它。
  • 文本except:except Exception 是否存在于代码中的其他任何位置?其中任何一个都可能在信号生成的异常到达执行do_stuff_before_python_terminates() 的处理程序之前捕获它。
  • 是的 - 如果您的代码当前正在另一个 try/except 中执行,则第一次有机会捕获 Exception。您所有的excepts 都需要尽可能使用最具体的异常名称,这样他们就不会捕获不应该捕获的东西。

标签: python sigint sigterm


【解决方案1】:

来自Execution of Python signal handlers

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

这可能是您的问题的原因。由于这个限制 (and others),用 Python 编写的信号处理程序除了在最简单的程序中外,很少能按预期运行。

如果您确实需要立即捕获信号,您可能需要使用调用 Python 代码的低级语言(例如 C)为您的程序编写一个包装器。

【讨论】:

  • 谢谢,但这对我来说是不可能的。我不能用 C 编写代码,我绝对需要在每个 sigterm 或 sigint(关机或 ctr+l)上运行 python run do_stuff_before_python_terminates()。
【解决方案2】:
  1. 在 main() 中注册信号。例如:

        def signal_handler(signal, frame):
                print('\n')
                sys.exit(0)
    
        def main():
            signal.signal(signal.SIGINT, signal_handler)
            do_stuff_before_python_terminates()
    
  2. 在低级 C 代码完成之前不会触发信号;来自文档:“纯粹在 C 中实现的长时间运行的计算(例如在大量文本上的正则表达式匹配)可以在任意时间内不间断地运行,无论接收到任何信号。Python 信号处理程序将被调用当计算完成时。”

【讨论】:

  • 如果我在我的代码进入 main() 之前全局注册它们有什么区别?我只有跳出while循环的问题
  • 没有“全局”——如果你有一个 main(),脚本从那里开始并从那里继续。
  • 我的代码确实从上到下工作,然后在我告诉他的代码末尾跳转到 main,在我注册 sigint 和 sigterm 处理程序的任何函数之外,就像在我的示例中一样代码
【解决方案3】:

您的 do_stuff_before_python_terminates() 调用不正确(至少根据我对信号和 python 的经验)。

在您的sigterm 和/或sigint 函数中调用do_stuff_before_python_terminates()

def do_stuff_before_python_terminates():
    save_variables_in_mysql()
    do_this_and_that()...

def sigterm(x, y):
    do_stuff_before_python_terminates()
    raise Exception()
def sigint(signal, frame):
    do_stuff_before_python_terminates()
    raise Exception()

确保将 do_stuff 函数放在 sigterm 和 sigint 函数之上。

【讨论】:

  • 这可能是编写信号处理程序的正确方法,但我想了解原始代码有什么问题。
  • 我添加了更多信息,谢谢你的回答我很感激,但你的回答没有帮助
猜你喜欢
  • 1970-01-01
  • 2015-08-15
  • 2015-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-08
相关资源
最近更新 更多