Ctrl-C即KeyboardInterrupt在Python中杀死线程

时间:2010-11-09 17:30:53

标签: python multithreading kill keyboardinterrupt

我在某处读到KeyboardInterrupt异常仅在Python的主线程中引发。我还读到在子线程执行时主线程被阻塞。那么,这是否意味着 CTRL + C 永远不能到达子线程。我尝试了以下代码:

def main():
    try:
        thread = threading.Thread(target=f)
        thread.start()  # thread is totally blocking (e.g., while True)
        thread.join()
    except KeyboardInterrupt:
        print "Ctrl+C pressed..."
        sys.exit(1)

def f():
    while True:
        pass  # do the actual work

在这种情况下, CTRL + C 对执行没有影响。它就像是无法收听信号。我理解这是错误的方式吗?有没有其他方法可以使用 CTRL + C 来杀死线程?

3 个答案:

答案 0 :(得分:13)

如果您希望主线程在加入时接收 CTRL + C 信号,可以通过向join()调用添加超时来完成。< / p>

以下似乎有效(如果您希望main实际结束,请不要忘记添加daemon=True):

thread1.start()
while True:
    thread1.join(600)
    if not thread1.isAlive():
        break

答案 1 :(得分:10)

问题在于您使用的是thread1.join(),这将导致程序等到该线程完成继续。

信号将始终被主进程捕获,因为它是接收信号的信号,它是具有线程的进程。

如你所示,你基本上运行一个没有线程功能的'普通'应用程序,因为你启动1个线程并等到它完成继续。

答案 2 :(得分:2)

在Python中,确实KeyboardInterrupt异常仅在每个进程的主线程中引发。但是正如提到的其他答案一样,方法Thread.join阻止了调用线程,包括包括 KeyboardInterrupt 异常,这也是事实。这就是为什么 Ctrl + C 似乎无效的原因:主线程中的执行在thread.join()行处仍然被阻止。

因此,对您的问题的一个简单解决方案是向Thread.join添加一个超时参数,并将其放入主线程的无限循环中,以便可以在每次超时后引发KeyboardInterrup异常,并使子线程daemonic,这意味着它的父线程(这里的主线程)在退出时将杀死它(只有非守护进程线程不会被杀死,而是在其父退出时加入):

def main():
    try:
        thread = threading.Thread(target=f, daemon=True)  # create a daemon child thread
        thread.start()

        while True:
            thread.join(1)  # join shortly to not block KeyboardInterrupt exceptions
    except KeyboardInterrupt:
        print "Ctrl+C pressed..."
        sys.exit(1)

def f():
    while True:
        pass  # do the actual work

但是,如果您控制子线程的代码,更好的解决方案是通知子线程正常退出(而不是像第一个解决方案那样突然退出),例如使用threading.Event

def main():
    try:
        event = threading.Event()
        thread = threading.Thread(target=f, args=(event,))
        thread.start()
        event.wait()  # wait forever but without blocking KeyboardInterrupt exceptions
    except KeyboardInterrupt:
        print "Ctrl+C pressed..."
        event.set()  # inform the child thread that it should exit
        sys.exit(1)

def f(event):
    while not event.is_set():
        pass  # do the actual work