如何使用 Ctrl+C 杀死子线程?

发布于 2024-10-01 05:26:47 字数 552 浏览 1 评论 0原文

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

I would like to stop the execution of a process with Ctrl+C in Python. But I have read somewhere that KeyboardInterrupt exceptions are only raised in the main thread. I have also read that the main thread is blocked while the child thread executes. So how can I kill the child thread?

For instance, Ctrl+C has no effect with the following code:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

当梦初醒 2024-10-08 05:26:48

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

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

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

If you want to have main thread to receive the CTRL+C signal while joining, it can be done by adding timeout to join() call.

The following seems to be working (don't forget to add daemon=True if you want main to actually end):

thread1.start()
while True:
    thread1.join(600)
    if not thread1.isAlive():
        break
撩发小公举 2024-10-08 05:26:48

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

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

正如您所展示的那样,您基本上正在运行一个“正常”应用程序,没有线程功能,因为您启动 1 个线程并等待它完成才能继续。

The problem there is that you are using thread1.join(), which will cause your program to wait until that thread finishes to continue.

The signals will always be caught by the main process, because it's the one that receives the signals, it's the process that has threads.

Doing it as you show, you are basically running a 'normal' application, without thread features, as you start 1 thread and wait until it finishes to continue.

书间行客 2024-10-08 05:26:48

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

KeyboardInterrupt exceptions are raised only in the main thread of each process. But the method Thread.join blocks the calling thread, including KeyboardInterrupt exceptions. That is why Ctrl+C seems to have no effect.

A simple solution to your problem is to make the method Thread.join time out to unblock KeyboardInterrupt exceptions, and make the child thread daemonic to let the parent thread kill it at exit (non-daemonic child threads are not killed but joined by their parent at exit):

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

A better solution if you control the code of the child thread is to notify the child thread to exit gracefully (instead of abruptly like with the simple solution), for instance using a 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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文