无法使用Ctrl-C终止Python脚本

问题:无法使用Ctrl-C终止Python脚本

我正在使用以下脚本测试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+ 将程序发送到后台后通过PID将其终止Z,这不会被忽略。为什么Ctrl+ C被如此持久地忽略?我该如何解决?

I am testing Python threading with the following script:

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()

This is running in Python 2.7 on Kubuntu 11.10. Ctrl+C will not kill it. I also tried adding a handler for system signals, but that did not help:

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

To kill the process I am killing it by PID after sending the program to the background with Ctrl+Z, which isn’t being ignored. Why is Ctrl+C being ignored so persistently? How can I resolve this?


回答 0

Ctrl+ C终止主线程,但是因为您的线程不在守护程序模式下,所以它们继续运行,并且使进程保持活动状态。我们可以使它们成为守护进程:

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

但是,还有另一个问题-一旦主线程启动了线程,就别无他法了。因此它退出了,线程立即被销毁。因此,让主线程保持活动状态:

import time
while True:
    time.sleep(1)

现在它将保留打印“ first”和“ second”,直到您按Ctrl+ 为止C

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

Ctrl+C terminates the main thread, but because your threads aren’t in daemon mode, they keep running, and that keeps the process alive. We can make them daemons:

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

But then there’s another problem – once the main thread has started your threads, there’s nothing else for it to do. So it exits, and the threads are destroyed instantly. So let’s keep the main thread alive:

import time
while True:
    time.sleep(1)

Now it will keep print ‘first’ and ‘second’ until you hit Ctrl+C.

Edit: as commenters have pointed out, the daemon threads may not get a chance to clean up things like temporary files. If you need that, then catch the KeyboardInterrupt on the main thread and have it co-ordinate cleanup and shutdown. But in many cases, letting daemon threads die suddenly is probably good enough.


回答 1

KeyboardInterrupt和信号仅在进程(即主线程)中可见…看看Ctrl-c即KeyboardInterrupt杀死python中的线程

KeyboardInterrupt and signals are only seen by the process (ie the main thread)… Have a look at Ctrl-c i.e. KeyboardInterrupt to kill threads in python


回答 2

我认为最好在您希望线程死亡时在线程上调用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()

I think it’s best to call join() on your threads when you expect them to die. I’ve taken some liberty with your code to make the loops end (you can add whatever cleanup needs are required to there as well). The variable die is checked for truth on each pass and when it’s True then the program exits.

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()

回答 3

@Thomas K的答案的改进版本:

  • 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)

An improved version of @Thomas K’s answer:

  • Defining an assistant function is_any_thread_alive() according to this gist, which can terminates the main() automatically.

Example codes:

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)