如何用Ctrl+C结束子线程?

24 投票
3 回答
42341 浏览
提问于 2025-04-16 06:48

我想在Python中通过按下 Ctrl+C 来停止一个进程的执行。但是我看到有说法提到,KeyboardInterrupt 这个异常只会在主线程中被触发。我还了解到,当子线程在执行时,主线程是被阻塞的。那么我该怎么结束子线程呢?

比如,下面的代码按下 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

3 个回答

8

KeyboardInterrupt 异常只会在每个进程的主线程中被触发。但是,Thread.join 方法会阻塞调用它的线程,这包括 KeyboardInterrupt 异常。这就是为什么按 Ctrl+C 似乎没有任何效果的原因。

解决这个问题的一个简单方法是让 Thread.join 方法设置超时,这样就可以解除对 KeyboardInterrupt 异常的阻塞,并将子线程设置为 守护线程,这样父线程在退出时可以杀掉它(非守护线程在退出时不会被杀掉,而是由父线程等待它结束):

def main():
    try:
        thread = threading.Thread(target=f)
        thread.daemon = True  # let the parent kill the child thread at exit
        thread.start()
        while thread.is_alive():
            thread.join(1)  # time out not to block KeyboardInterrupt
    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 without blocking KeyboardInterrupt
    except KeyboardInterrupt:
        print "Ctrl+C pressed..."
        event.set()  # notify the child thread to exit
        sys.exit(1)

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

如果你想让主线程在等待其他线程结束的时候能够接收到 CTRL+C 的信号,可以通过给 join() 方法加上超时来实现。

下面的代码看起来是有效的(如果你希望主线程真的能结束,别忘了加上 daemon=True):

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

这里的问题是你在使用 thread1.join(),这会导致你的程序在这个线程完成之前一直等待,不能继续执行。

信号总是会被主进程接收到,因为信号是由主进程接收的,主进程才有线程。

按照你展示的方式,你实际上是在运行一个“普通”的应用程序,没有使用线程的特性,因为你只启动了一个线程,并且在它完成之前程序就停在那里不动。

撰写回答