如何从Python线程中退出整个应用程序?

105 投票
7 回答
123844 浏览
提问于 2025-04-15 14:39

我该如何从我的Python应用程序中的一个线程退出整个应用呢?调用sys.exit()只会结束它被调用的那个线程,这样对我没什么帮助。

我不想使用os.kill()这种方法,因为这看起来不太干净。

7 个回答

28

使用 thread.interrupt_main() 在某些情况下可能没什么用。KeyboardInterrupt 通常是在命令行应用中用来退出当前命令或者清理输入行的。

另外,os._exit 会立即终止进程,而不会执行你代码中的任何 finally 块,这可能会很危险(比如文件和连接不会被关闭)。

我找到的解决办法是在主线程中注册一个信号处理器,这个处理器会引发一个自定义的异常。然后用后台线程来发送这个信号。

import signal
import os
import threading
import time


class ExitCommand(Exception):
    pass


def signal_handler(signal, frame):
    raise ExitCommand()


def thread_job():
    time.sleep(5)
    os.kill(os.getpid(), signal.SIGUSR1)


signal.signal(signal.SIGUSR1, signal_handler)
threading.Thread(target=thread_job).start()  # thread will fire in 5 seconds
try:
    while True:
        user_input = raw_input('Blocked by raw_input loop ')
        # do something with 'user_input'
except ExitCommand:
    pass
finally:
    print('finally will still run')

相关问题:

115

简单来说:使用 os._exit

详细解释和示例:

我从DevShed的一个教程中拿了一个简单的线程示例,并稍微修改了一下:

import threading, sys, os

theVar = 1

class MyThread ( threading.Thread ):

   def run ( self ):

      global theVar
      print 'This is thread ' + str ( theVar ) + ' speaking.'
      print 'Hello and good bye.'
      theVar = theVar + 1
      if theVar == 4:
          #sys.exit(1)
          os._exit(1)
      print '(done)'

for x in xrange ( 7 ):
   MyThread().start()

如果你把 sys.exit(1) 注释掉,脚本会在第三个线程打印完后结束。如果你使用 sys.exit(1) 并注释掉 os._exit(1),那么第三个线程就不会打印 (done),程序会继续运行所有七个线程。

os._exit “通常只应该在 fork() 之后的子进程中使用”——而一个单独的线程在这个情况下也差不多可以这样用。另外要注意,在 os._exit 的手册页面后面列出了几个枚举值,你应该优先使用这些值作为 os._exit 的参数,而不是像我在上面的例子中用的简单数字。

70

如果你除了主线程之外的所有线程都是守护线程,那么通常最好的办法是使用 thread.interrupt_main()。任何线程都可以用这个方法在主线程中引发一个 KeyboardInterrupt,这样通常可以让主线程比较干净地退出(包括主线程中的一些清理工作会被调用等)。

当然,如果这样做导致某个非守护线程让整个程序一直运行下去,那么你需要按照Mark的建议使用 os._exit。但我认为这应该是最后的手段(有点像 kill -9;-)因为这样会很粗暴地结束程序(不会执行清理工作,包括 try/finally 块、 with 块、 atexit 函数等)。

撰写回答