【问题标题】:How to kill a child thread with Ctrl+C?如何使用 Ctrl+C 杀死子线程?
【发布时间】:2010-11-09 17:30:52
【问题描述】:

我想在 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

【问题讨论】:

  • 您的主线程没有被阻塞,因为您启动了另一个线程。如果这是真的,线程的意义何在?这是因为您正在调用 thread1.join(),它会阻塞直到 thread1 完成。

标签: python multithreading kill keyboardinterrupt


【解决方案1】:

如果你想让主线程在加入时接收CTRL+C信号,可以通过在join()调用中添加超时来完成。

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

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

【讨论】:

    【解决方案2】:

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

    信号总是会被主进程捕获,因为它是接收信号的那个,它是有线程的进程。

    按照您的说明进行操作,您基本上是在运行一个“普通”应用程序,没有线程功能,因为您启动 1 个线程并等待它完成继续。

    【讨论】:

    • 但是主线程还是收到退出信号吧?那么为什么它不将信号发送给它的孩子并且都退出呢?
    【解决方案3】:

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

    解决您的问题的一个简单方法是使方法Thread.join 超时以解除阻塞KeyboardInterrupt 异常,并使子线程daemonic 让父线程在退出时将其杀死(非守护子线程是没有被杀死,但在退出时被他们的父母加入):

    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
    

    【讨论】:

    • 这似乎对我不起作用:使用 Python 3.8.x。据我所知,KeyboardInterrupt 似乎无法捕捉到。在其他代码中,当键盘中断发生时,我会收到“异常被忽略”的提示。
    • @mikerodent 你试过这个答案中的两个例子吗?
    • 要删除我的评论...我正在慢慢了解这些东西是如何工作的。我对正常关闭和对 Ctrl-C (stackoverflow.com/a/66949660/595305) 的响应的解决方案:从我的实验看来,主线程上的 while True: 循环是必要的......
    • @Maggyero 这是什么信号?
    猜你喜欢
    • 2021-02-23
    • 2019-11-13
    • 2019-07-13
    • 2017-11-11
    • 2011-06-29
    • 1970-01-01
    • 2011-02-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多