【问题标题】:Why is signal.SIGTERM not dealt with properly in my main thread?为什么我的主线程中没有正确处理 signal.SIGTERM?
【发布时间】:2013-11-28 02:48:30
【问题描述】:

我有连续运行的 python 代码(收集传感器数据)。它应该在启动时使用start-stop-daemon 启动。但是,我希望能够优雅地终止进程,所以我从How to process SIGTERM signal gracefully? 帖子中的建议开始,并将我的主循环放在一个单独的线程中。我希望能够在它作为守护进程运行时优雅地关闭它(start-stop-daemon 将发送一个终止信号)以及当我短暂启动它以自己在终端中进行测试时(我按下 ctrl-c) .

但是,如果我终止进程,似乎不会调用信号处理程序(即使不使用线程,“done (killed)”也不会出现在我重定向到的文件中)。当我按下ctrl-c 时,收集将继续并在终端中(或我重定向到的文件)保持打印数据。

我在下面的代码中做错了什么?

from threading import Thread
import time, sys, signal

shutdown_flag = False #used for gracefull shutdown 

def main_loop():
    while not shutdown_flag:
        collect_data() # contains some print "data" statements
        time.sleep(5)
    print "done (killed)"

def sighandler(signum, frame):
    print 'signal handler called with signal: %s ' % signum
    global shutdown_flag
    shutdown_flag = True

def main(argv=None):
    signal.signal(signal.SIGTERM, sighandler) # so we can handle kill gracefully
    signal.signal(signal.SIGINT, sighandler) # so we can handle ctrl-c
    try:
        Thread(target=main_loop, args=()).start()
    except Exception, reason:
        print reason

if __name__ == '__main__':
    sys.exit(main(sys.argv))

【问题讨论】:

    标签: python multithreading sigterm


    【解决方案1】:

    你正在用这个语句终止你的主线程:

    if __name__ == '__main__':
        sys.exit(main(sys.argv))
    

    所以你的信号处理程序永远不会运行。信号处理程序是主线程的一部分,而不是您创建的 main_loop 线程。所以一旦主线程退出,就没有信号处理函数可以调用了。

    你需要这样的东西:

    def sighandler(signum, frame):
        print 'signal handler called with signal: %s ' % signum
        global shutdown_flag
        shutdown_flag = True
        sys.exit() # make sure you add this so the main thread exits as well.
    
    if __name__ == '__main__':
        main(sys.argv)
        while 1:  # this will force your main thread to live until you terminate it.
           time.sleep(1) 
    

    查看程序中正在运行多少线程的简单测试如下:

    def main_loop():
        while not shutdown_flag:
            collect_data() # contains some print "data" statements
            time.sleep(5)
            import threading
            print threading.enumerate()
        print "done (killed)"
    

    【讨论】:

    • 但是主线程怎么能结束呢? (确实,发送kill <pid> 会停止向文件输出数据,但是当我发送ps 时,python 进程仍然以相同的 列出)。另外,我想我在某处读到过,当使用Thread(..).start() 时,调用线程等待生成的线程完成(与thread 库中的thread.start_new_thread 相比)
    • 可以将线程标记为“守护线程”。这个标志的意义在于,当只剩下守护线程时,整个 Python 程序就退出了。初始值继承自创建线程。该标志可以使用 setDaemon() 方法设置并使用 isDaemon() 方法检索。
    • 主线程,在这种情况下不等待其他线程完成。即使它是守护线程,如果您退出主线程,它也会等待其他线程完成,但不会执行主线程中的代码。因此,您将无法调用 sig 处理程序。你需要保持你的主线程活着。
    • 嗯,这有点工作,但不完全。当我从另一个 ssh 会话执行 kill <pid> 时,确实我看到 main_loop 线程已停止(并且“完成(已终止)”)被打印出来。但主线程仍在运行,信号处理程序现在正确打印调用的信号。无论我是否将 main_loop 线程作为守护进程启动,都不会改变这种行为。我看到的行为对我来说似乎是合乎逻辑的,因为如果我们有一个 while 1 循环,主线程就无法结束,不是吗?所以问题仍然存在......
    • 哎呀,是的,我忘了你在sighandler 函数的末尾需要一个sys.exit()。这应该会强制主循环退出
    猜你喜欢
    • 2021-05-19
    • 1970-01-01
    • 2011-06-19
    • 2020-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-21
    • 1970-01-01
    相关资源
    最近更新 更多