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

时间:2022-03-26 20:40:47

This could be a stupid question, but I'm testing out some of my assumptions about Python and I'm confused as to why the following code snippet would not exit when called in the thread, but would exit when called in the main thread.

这可能是一个愚蠢的问题,但我正在测试我对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"

The docs for sys.exit() state that the call should exit from Python. I can see from the output of this program that "post thread exit" is never printed, but the main thread just keeps on going even after the thread calls exit.

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

Is a separate instance of the interpreter being created for each thread, and the call to exit() is just exiting that separate instance? If so, how does the threading implementation manage access to shared resources? What if I did want to exit the program from the thread (not that I actually want to, but just so I understand)?

是为每个线程创建一个单独的解释器实例,而对exit()的调用只是退出该单独的实例吗?如果是这样,线程实现如何管理对共享资源的访问?如果我确实想要从线程中退出程序(不是我真的想要,但我只是理解),该怎么办?

5 个解决方案

#1


50  

sys.exit() raises the SystemExit exception, as does thread.exit(). So, when sys.exit() raises that exception inside that thread, it has the same effect as calling thread.exit(), which is why only the thread exits.

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

#2


12  

What if I did want to exit the program from the thread (not that I actually want to, but just so I understand)?

如果我确实想要从线程中退出程序(不是我真的想要,但我只是理解),该怎么办?

At least on Linux you could do something like:

至少在Linux上你可以这样做:

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

This send a SIGINT to the main thread where it can be handled as a KeyboardInterrupt.

这会将SIGINT发送到主线程,在那里它可以作为KeyboardInterrupt处理。

BTW: On Windows you can only send a SIGTERM signal, which cannot be caught from Python. (there I would prefer calling os._exit as suggested by Helmut)

顺便说一句:在Windows上,你只能发送一个SIGTERM信号,这个信号无法从Python中捕获。 (我更喜欢按Helmut的建议调用os._exit)

#3


11  

What if I did want to exit the program from the thread (not that I actually want to, but just so I understand)?

如果我确实想要从线程中退出程序(不是我真的想要,但我只是理解),该怎么办?

My preferred method is Erlang-ish message passing. Slightly simlified, I do it like this:

我首选的方法是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

#4


10  

What if I did want to exit the program from the thread?

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

Apart from the method Deestan described you can call os._exit (notice the underscore). Before using it make sure that you understand that it does no cleanups (like calling __del__ or similar).

除了Deestan描述的方法,你可以调用os._exit(注意下划线)。在使用它之前,请确保您了解它没有清理(例如调用__del__或类似)。

#5


8  

Is the fact that "pre main exit, post thread exit" is printed what's bothering you?

事实上,“主要出口前,出线后退出”是否印刷了什么困扰你?

Unlike some other languages (like Java) where the analog to sys.exit (System.exit, in Java's case) causes the VM/process/interpreter to immediately stop, Python's sys.exit just throws an exception: a SystemExit exception in particular.

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

Here are the docs for sys.exit (just print sys.exit.__doc__):

以下是sys.exit的文档(只是打印sys.exit .__ doc__):

Exit the interpreter by raising SystemExit(status).
If the status is omitted or None, it defaults to zero (i.e., success).
If the status is numeric, it will be used as the system exit status.
If it is another kind of object, it will be printed and the system
exit status will be one (i.e., failure).

通过提升SystemExit(状态)退出解释器。如果省略状态或None,则默认为零(即成功)。如果状态为数字,则将其用作系统退出状态。如果它是另一种对象,它将被打印并且系统退出状态将是一个(即,失败)。

This has a few consequences:

这会产生一些后果:

  • in a thread it just kills the current thread, not the entire process (assuming it gets all the way to the top of the stack...)
  • 在一个线程中它只会杀死当前线程,而不是整个进程(假设它一直到达堆栈顶部......)
  • object destructors (__del__) are potentially invoked as the stack frames that reference those objects are unwound
  • 可能会调用对象析构函数(__del__),因为引用这些对象的堆栈帧已展开
  • finally blocks are executed as the stack unwinds
  • 最后块在堆栈展开时执行
  • you can catch a SystemExit exception
  • 您可以捕获SystemExit异常

The last is possibly the most surprising, and is yet another reason why you should almost never have an unqualified except statement in your Python code.

最后一个可能是最令人惊讶的,这也是你在Python代码中几乎从不会有一个不合格的except语句的另一个原因。

#1


50  

sys.exit() raises the SystemExit exception, as does thread.exit(). So, when sys.exit() raises that exception inside that thread, it has the same effect as calling thread.exit(), which is why only the thread exits.

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

#2


12  

What if I did want to exit the program from the thread (not that I actually want to, but just so I understand)?

如果我确实想要从线程中退出程序(不是我真的想要,但我只是理解),该怎么办?

At least on Linux you could do something like:

至少在Linux上你可以这样做:

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

This send a SIGINT to the main thread where it can be handled as a KeyboardInterrupt.

这会将SIGINT发送到主线程,在那里它可以作为KeyboardInterrupt处理。

BTW: On Windows you can only send a SIGTERM signal, which cannot be caught from Python. (there I would prefer calling os._exit as suggested by Helmut)

顺便说一句:在Windows上,你只能发送一个SIGTERM信号,这个信号无法从Python中捕获。 (我更喜欢按Helmut的建议调用os._exit)

#3


11  

What if I did want to exit the program from the thread (not that I actually want to, but just so I understand)?

如果我确实想要从线程中退出程序(不是我真的想要,但我只是理解),该怎么办?

My preferred method is Erlang-ish message passing. Slightly simlified, I do it like this:

我首选的方法是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

#4


10  

What if I did want to exit the program from the thread?

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

Apart from the method Deestan described you can call os._exit (notice the underscore). Before using it make sure that you understand that it does no cleanups (like calling __del__ or similar).

除了Deestan描述的方法,你可以调用os._exit(注意下划线)。在使用它之前,请确保您了解它没有清理(例如调用__del__或类似)。

#5


8  

Is the fact that "pre main exit, post thread exit" is printed what's bothering you?

事实上,“主要出口前,出线后退出”是否印刷了什么困扰你?

Unlike some other languages (like Java) where the analog to sys.exit (System.exit, in Java's case) causes the VM/process/interpreter to immediately stop, Python's sys.exit just throws an exception: a SystemExit exception in particular.

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

Here are the docs for sys.exit (just print sys.exit.__doc__):

以下是sys.exit的文档(只是打印sys.exit .__ doc__):

Exit the interpreter by raising SystemExit(status).
If the status is omitted or None, it defaults to zero (i.e., success).
If the status is numeric, it will be used as the system exit status.
If it is another kind of object, it will be printed and the system
exit status will be one (i.e., failure).

通过提升SystemExit(状态)退出解释器。如果省略状态或None,则默认为零(即成功)。如果状态为数字,则将其用作系统退出状态。如果它是另一种对象,它将被打印并且系统退出状态将是一个(即,失败)。

This has a few consequences:

这会产生一些后果:

  • in a thread it just kills the current thread, not the entire process (assuming it gets all the way to the top of the stack...)
  • 在一个线程中它只会杀死当前线程,而不是整个进程(假设它一直到达堆栈顶部......)
  • object destructors (__del__) are potentially invoked as the stack frames that reference those objects are unwound
  • 可能会调用对象析构函数(__del__),因为引用这些对象的堆栈帧已展开
  • finally blocks are executed as the stack unwinds
  • 最后块在堆栈展开时执行
  • you can catch a SystemExit exception
  • 您可以捕获SystemExit异常

The last is possibly the most surprising, and is yet another reason why you should almost never have an unqualified except statement in your Python code.

最后一个可能是最令人惊讶的,这也是你在Python代码中几乎从不会有一个不合格的except语句的另一个原因。