113

这可能是一个愚蠢的问题,但我正在测试我对 Python 的一些假设,我很困惑为什么以下代码片段在线程中调用时不会退出,但在主线程中调用时会退出。

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() 的调用只是退出该单独的实例?如果是这样,线程实现如何管理对共享资源的访问?如果我确实想从线程中退出程序怎么办(不是我真正想要的,但我明白了)?

4

6 回答 6

86

sys.exit()引发SystemExit异常,thread.exit(). 因此,当sys.exit()在该线程内引发该异常时,它与调用具有相同的效果thread.exit(),这就是只有线程退出的原因。

于 2009-05-25T03:31:38.480 回答
32

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

对于 Linux:

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

这会将 a 发送SIGINT到引发 a 的主线程KeyboardInterrupt。这样你就有了适当的清理工作。如果您想做出不同的反应,您也可以注册一个处理程序。

以上不适用于 Windows,因为您只能发送一个SIGTERM信号,该信号不由 Python 处理,与os._exit().

对于 Windows:

您可以使用:

os._exit()

这将退出整个过程而不进行任何清理。如果需要清理,则需要以另一种方式与主线程通信。

于 2011-08-17T20:43:41.887 回答
26

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

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

于 2011-02-25T16:58:10.660 回答
13

如果我确实想从线程中退出程序怎么办(不是我真正想要的,但我明白了)?

我首选的方法是 Erlang-ish 消息传递。稍微简化,我这样做:

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
于 2009-05-25T11:32:18.467 回答
12

打印“pre main exit, post thread exit”的事实让你感到困扰吗?

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

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

通过引发 SystemExit(status) 退出解释器。
如果省略状态或无,则默认为零(即成功)。
如果状态是数字,它将被用作系统退出状态。
如果是另一种对象,则打印出来,系统
退出状态为一(即失败)。

这有几个后果:

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

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

于 2009-05-25T04:07:21.217 回答
2

_thread.interrupt_main()从 Python 3.7 开始可用(在此之前可选)

于 2021-01-05T06:29:26.660 回答