【问题标题】:Cannot kill Python script with Ctrl-C无法使用 Ctrl-C 终止 Python 脚本
【发布时间】:2012-08-05 11:16:13
【问题描述】:

我正在使用以下脚本测试 Python 线程:

import threading

class FirstThread (threading.Thread):
    def run (self):
        while True:
            print 'first'

class SecondThread (threading.Thread):
    def run (self):
        while True:
            print 'second'

FirstThread().start()
SecondThread().start()

这是在 Kubuntu 11.10 上的 Python 2.7 中运行的。 Ctrl+C 不会杀死它。我还尝试为系统信号添加处理程序,但这没有帮助:

import signal 
import sys
def signal_handler(signal, frame):
    sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)

为了杀死进程,我在使用 Ctrl+Z 将程序发送到后台后通过 PID 杀死它,这不会被忽略。为什么 Ctrl+C 会被如此持久地忽略?我该如何解决这个问题?

【问题讨论】:

  • @dotancohen 它在 Windows 上工作吗?
  • @vitaibian:我没有在 Windows 上测试过,但它似乎与操作系统无关。

标签: python linux


【解决方案1】:

Ctrl+C 会终止主线程,但是由于您的线程未处于守护程序模式,它们会继续运行,从而使进程保持活动状态。我们可以让它们成为守护进程:

f = FirstThread()
f.daemon = True
f.start()
s = SecondThread()
s.daemon = True
s.start()

但是还有另一个问题——一旦主线程启动了你的线程,它就没有其他事情要做了。所以它退出了,线程立即被销毁。所以让我们保持主线程活着:

import time
while True:
    time.sleep(1)

现在它将保持打印“第一”和“第二”,直到您点击 Ctrl+C

编辑:正如评论者所指出的,守护线程可能没有机会清理诸如临时文件之类的东西。如果需要,请在主线程上捕获 KeyboardInterrupt 并让它协调清理和关闭。但在很多情况下,让守护线程突然死掉可能就足够了。

【讨论】:

  • 你应该提到,通过这样做,线程并没有优雅地停止,一些资源也没有释放。
  • 嗯,Ctrl-C 从来都不是停止任何事情的优雅方式。我不确定会留下什么资源 - 进程退出时操作系统不应该回收任何东西吗?
  • @ThomasK 例如,tempfile.TemporaryFile() 创建的临时文件可能会留在磁盘上。
  • @deed02392 :我不知道主线程到底发生了什么,但据我所知,在它退出后你不能对它做任何事情。当所有非守护线程都完成时,该进程将完成;父子关系不会出现。
  • 在 python3 中看起来像 you can pass daemon=TrueThread.__init__
【解决方案2】:

KeyboardInterrupt 和信号只有进程(即主线程)才能看到...看看Ctrl-c i.e. KeyboardInterrupt to kill threads in python

【讨论】:

    【解决方案3】:

    我认为当您希望线程死亡时,最好在线程上调用join()。我冒昧地将您的循环更改为结束(您也可以在那里添加所需的任何清理需求)。每次传递都会检查变量die,当它是True 时,程序退出。

    import threading
    import time
    
    class MyThread (threading.Thread):
        die = False
        def __init__(self, name):
            threading.Thread.__init__(self)
            self.name = name
    
        def run (self):
            while not self.die:
                time.sleep(1)
                print (self.name)
    
        def join(self):
            self.die = True
            super().join()
    
    if __name__ == '__main__':
        f = MyThread('first')
        f.start()
        s = MyThread('second')
        s.start()
        try:
            while True:
                time.sleep(2)
        except KeyboardInterrupt:
            f.join()
            s.join()
    

    【讨论】:

    • while True 很傻,你应该直接join - 而且这个被覆盖的函数有点可疑。也许def join(self, force=False): if force: self.die = True 这样join() 不会被join(force=True) 改变而杀死他们。但即便如此,最好在加入任一线程之前通知两个线程。
    【解决方案4】:

    @Thomas K's answer的改进版:

    • 根据this gist定义一个助手函数is_any_thread_alive(),可以自动终止main()

    示例代码:

    import threading
    
    def job1():
        ...
    
    def job2():
        ...
    
    def is_any_thread_alive(threads):
        return True in [t.is_alive() for t in threads]
    
    if __name__ == "__main__":
        ...
        t1 = threading.Thread(target=job1,daemon=True)
        t2 = threading.Thread(target=job2,daemon=True)
        t1.start()
        t2.start()
    
        while is_any_thread_alive([t1,t2]):
            time.sleep(0)
    

    【讨论】:

      【解决方案5】:

      要提防一个简单的“陷阱”,您确定 CAPS LOCK 没有打开吗?

      我在 Pi4 上的 Thonny IDE 中运行 Python 脚本。 CAPS LOCK 打开时,Ctrl+Shift+C 被传递到键盘缓冲区,而不是 Ctrl +C.

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-12-30
        • 2018-07-11
        • 2019-12-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-23
        相关资源
        最近更新 更多