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


117

我正在使用以下脚本测试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被如此持久地忽略?我该如何解决?


@dotancohen在Windows上可以使用吗?
kiriloff 2012年

@vitaibian:我尚未在Windows上进行测试,但似乎不是特定于OS的。
dotancohen 2012年

Answers:


178

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主线程并协调其清理和关闭。但是在很多情况下,让守护线程突然死掉可能已经足够了。


6
您应该提到这样做不会使线程正常停止,并且不会释放某些资源。
Tommaso Barbugli 2013年

1
好吧,Ctrl-C绝不是停止任何操作的优雅方式。我不确定会剩下什么资源-进程退出时,操作系统是否不应回收任何东西?
Thomas K

7
例如,@ ThomasK创建的临时文件tempfile.TemporaryFile()可能留在磁盘上。
Feuermurmel

1
@ deed02392:我不知道主线程到底发生了什么,但据我所知,退出后,您将无法对其执行任何操作。当所有非守护进程线程都完成时,该过程将完成。亲子关系不属于这种情况。
Thomas K

4
看起来像在python3中,您可以传递daemon=TrueThread.__init__
Ryan Haining


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)杀死他们。但是即使那样,最好在加入其中一个线程之前通知两个线程。
o11c

0

@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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.