为什么在 Python 中的线程内调用 sys.exit()时不退出?

我很困惑,为什么下面的代码片段在线程中调用时不会退出,而在主线程中调用时会退出。

import sys, time
from threading import Thread


def testexit():
time.sleep(5)
sys.exit()
print "post thread exit"


t = Thread(target = testexit)
t.start()
t.join()
print "pre main exit, post thread exit"
sys.exit()
print "post main exit"

sys.exit()的文档声明调用应该从 Python 退出。我可以从这个程序的输出中看到,“ post thread exit”从来没有被打印出来,但是主线程在线程调用 exit 之后仍然继续运行。

是否为每个线程创建一个单独的解释器实例,并且对 exit()的调用正在退出该单独的实例?如果是,线程实现如何管理对共享资源的访问?如果我确实想从线程中退出程序(并不是我真的想退出,只是为了让我理解) ,那该怎么办?

90866 次浏览

sys.exit()引发 SystemExit异常,thread.exit()也是如此。因此,当 sys.exit()在该线程内引发该异常时,其效果与调用 thread.exit()相同,这就是为什么只有该线程退出。

事实上,“前主退出,后线程退出”是打印什么困扰你?

与其他一些语言(比如 Java)不同的是,sys.exit的模拟(在 Java 中是 System.exit)会导致 VM/process/解释器立即停止,Python 的 sys.exit只是抛出了一个异常: 特别是 SystemExit 异常。

下面是 sys.exit(只有 print sys.exit.__doc__)的文档:

通过引发 SystemExit (status)退出解释器。
If the status is omitted or None, it defaults to zero (i.e., success).
如果状态为数字,则将其用作系统退出状态。
如果它是另一种对象,它将被打印和系统
退出状态为1(即,失败)。 < br >

这会产生一些后果:

  • 在线程中,它只杀死当前线程,而不杀死整个进程(假设它一直到达堆栈的顶部...)
  • 对象析构函数(__del__)可能会作为引用这些对象的堆栈帧被调用
  • 在堆栈展开时执行 finally 块
  • 可以捕获 SystemExit异常

最后一个可能是最令人惊讶的,这也是为什么在 Python 代码中几乎不应该有不限定的 except语句的另一个原因。

如果我真的想退出这个项目呢 从线程(不是说我实际上 想,但只是为了让我明白) ?

我首选的方法是 Erlang 式的消息传递:

import sys, time
import threading
import Queue # thread-safe


class CleanExit:
pass


ipq = Queue.Queue()


def testexit(ipq):
time.sleep(5)
ipq.put(CleanExit)
return


threading.Thread(target=testexit, args=(ipq,)).start()
while True:
print "Working..."
time.sleep(1)
try:
if ipq.get_nowait() == CleanExit:
sys.exit()
except Queue.Empty:
pass

What if I did want to exit the program 从帖子里?

除了 Deestan 描述的方法之外,您还可以调用 os._exit(请注意下划线)。在使用它之前,请确保您了解它执行了 no清理(比如调用 __del__或类似的操作)。

如果我确实想从线程中退出程序,该怎么办?

对于 Linux:

os.kill(os.getpid(), signal.SIGINT)

这将向引发 KeyboardInterrupt的主线程发送一个 SIGINT。有了这个,你得好好清理一下。此外,如果您希望做出不同的反应,还可以注册一个处理程序。

视窗:

上面的方法在 Windows 上不起作用,因为您只能发送一个 SIGTERM信号,而这个信号不是由 Python 处理的,并且具有与 os._exit()相同的效果。

唯一的选择是:

os._exit()

This will exit the entire process without any cleanup. If you need cleanup, you need to communicate with the main thread in another way.

_thread.interrupt_main()在 Python 3.7之后就可以使用了(在那之前是可选的)