代码之家  ›  专栏  ›  技术社区  ›  Rohit

Python2:守护进程线程不会在键盘中断时被终止

  •  0
  • Rohit  · 技术社区  · 6 年前

    我正在编写一个简单的线程应用程序,并将线程设置为 daemon 因为我希望我的程序退出 KeyboardInterrupt . 这工作良好,并给出了预期的结果 python3 但是 python2.7 似乎不尊重 守护进程 旗帜。下面是我的示例代码

    if __name__ == '__main__':
        try:
            threads = [Thread(target=some_func, args=(arg1,arg2)) for _ in range(10)]
    
            for th in threads:
                th.daemon=True
                th.start()
    
            for th in threads:
                th.join()
        except KeyboardInterrupt:
            sys.exit('Ctrl-c issued by user .. Exiting')
    

    当我运行此代码时 蟒蛇3 然后击中 ctrl-c 过了一会儿,我的程序按预期退出,但是当我运行这个程序时 蟒蛇2.7 然后击中 ctrl-c键 它永远不会退出,我必须从壳牌杀掉这个过程。

    我是不是丢了什么东西?我也试过设置 threading.Event 然后清除事件 键盘中断 发生,但即使这样也不起作用

    蟒蛇3 如果我没有 join 我的 守护进程 线程,然后他们会退出,只要我的程序完成,如果我没有标记我的线程 守护进程 别这样 参加 然后程序继续,一旦我点击 ctrl-c键 它退出。但这些都不起作用 蟒蛇2.7

    编辑1

    我又做了些调查 蟒蛇2.7 无法捕获 键盘中断 . 更奇怪的是因为 键盘中断 很好,它是一个非线程程序,但是对于线程 键盘中断 即使线程标记为 守护进程 .

    所有这些都发生在 蟒蛇2.7 蟒蛇3 工作得很好。

    3 回复  |  直到 6 年前
        1
  •  1
  •   MisterMiyagi    6 年前

    方法 Thread.join 在较旧版本的Python中工作时可以静默地接受信号,直到 join 完成。这包括SIGINT aka KeyboardInterrupt。

    作为解决办法,你可以尝试 sleep 参加 . 如果您希望您的线程自行消亡,请确保清理:

        while threads:
            threads[-1].join(1)  # allow KeyboardInterrup once per second
            if not threads[-1].is_alive():  # reap dead Threads
               threads.pop()
    

    我的第一个想法是使用thread.join,但这似乎会阻塞主进程(忽略KeyboardInterrupt),直到线程完成。 1


    多线程Python程序通常忽略键盘中断生成的SIGINT,特别是当获取信号的线程正在等待或休眠时。 2


    我使用的是Python2.7,我的程序在使用时忽略了SIGTERM信号 线程池执行器。

    经过一些调试,我发现我的主线程在thread.join()上被阻塞, 它保持中断直到调用返回。 3

        2
  •  1
  •   Rohit    6 年前

    感谢@MisterMiyagi的建议,我能够测试 https://stackoverflow.com/a/3788243/5349916 .

    请注意以下解决方案仅适用于 daemon 线程,因为如果将 join 在上面。如果你不把 参加 在非守护进程线程中,如果主服务器退出,它们将继续在后台运行,这是预期的行为。

    我的问题是为了 守护进程 线。下面是解决办法。

    #一: 不要在线程上放置连接,因为它会导致线程阻塞主进程。工作是因为 守护进程 当主程序退出时,线程退出,因此当您不放置 参加 那你就不用等主程序了 time.sleep(1) 里面 while 只是为了确保 守护进程 线程继续执行,一旦我点击 ctrl-c 主会退出 守护进程 穿上它。

    if __name__ == "__main__":
        try:
            threads = [threading.Thread(name='daemon', target=daemon)
                       for _ in range(8)]
    
            for th in threads:
                th.daemon = True
                th.start()
    
        while True:
            time.sleep(1)
        # signal.pause()    # This worked fine for me on Linux, on Windows it doesn't work, not sure about Mac. I am not recommending this because of platform dependency.
    
        except KeyboardInterrupt:
    

    #二: 如果你想 参加 在线程上,对其使用timeout,然后将其保持在 虽然 循环检查直到 thread alive . 这将继续检查每个线程 threads 列出它,直到它还活着,然后等待1秒执行它。从现在起 虽然 情况会是 True 直到 Thread 实际上是在它完成之后收获的;它将以1秒为单位的小块阻塞主程序,并且一旦您点击 ctrl-c键 这个 daemon thread 不再活着,因此主程序退出。

    if __name__ == "__main__":
        try:
            threads = [threading.Thread(name='daemon', target=daemon)
                       for _ in range(8)]
            for th in threads:
                th.daemon = True
                th.start()
    
            for th in threads:
                while th.isAlive():
                    th.join(1)
    
        except KeyboardInterrupt:
            print('ctrl-c')
    
        3
  •  -1
  •   meep    6 年前

    这是由于“th.join()”而发生的。这使得他们基本上都在等待对方。你有没有试过使用类似的东西:

    except KeyboardInterrupt:
        for th in threads:
            th._Thread__stop()
        sys.exit('....')
    

    当我重现这个问题时,这对我很有用。希望有帮助! 另外,如果有更多的帮助,请阅读这篇文章(不是双关语!),因为它提供了一些有用的信息:

    Is there any way to kill a Thread?