问题:为什么在Python的线程内调用sys.exit()时不退出?
这可能是一个愚蠢的问题,但是我正在测试我对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()的调用只是退出了该单独的实例?如果是这样,线程实现如何管理对共享资源的访问?如果我确实想从线程中退出程序怎么办(不是我真正想要的,但据我所知)?
回答 0
sys.exit()
和一样引发SystemExit
异常thread.exit()
。因此,当sys.exit()
在该线程内引发该异常时,它的作用与调用相同thread.exit()
,这就是为什么只有该线程退出的原因。
回答 1
如果我确实想从线程中退出程序怎么办?
除了Deestan描述的方法之外,您还可以调用os._exit
(注意下划线)。使用之前,请确保你明白,它没有清理(如呼叫__del__
或类似)。
回答 2
如果我确实想从线程中退出程序怎么办(不是我真正想要的,但据我所知)?
至少在Linux上,您可以执行以下操作:
os.kill(os.getpid(), signal.SIGINT)
这会将a发送SIGINT
到主线程,并引发a KeyboardInterrupt
。这样您就可以进行适当的清理了。如果需要,您甚至可以处理信号。
顺便说一句:在Windows上,您只能发送SIGTERM
信号,而Python无法捕获该信号。在这种情况下,您可以简单地使用os._exit
相同的效果。
回答 3
是“打印前主出口,线程后出口”的事实困扰着您吗?
与其他语言(如Java)不同,其他语言(如Java)的类似物sys.exit
(System.exit
在Java中为Java)会导致VM /进程/解释器立即停止,而Python sys.exit
只是抛出一个异常:特别是SystemExit异常。
以下是sys.exit
(just print sys.exit.__doc__
)的文档:
通过提高SystemExit(status)退出解释器。
如果省略状态或无,则默认为零(即成功)。
如果状态为数字,则将其用作系统退出状态。
如果是另一种对象,则将其打印出来,并且系统
退出状态将为1(即失败)。
这会带来一些后果:
- 在一个线程中,它只是杀死当前线程,而不是杀死整个进程(假设它一直到达栈顶…)
__del__
当引用那些对象的堆栈框架被展开时,可能会调用对象析构函数()- 最后,在堆栈展开时执行块
- 你可以抓住一个
SystemExit
exceptions
最后一个可能是最令人惊讶的,也是另一个原因,为什么您几乎不应except
在Python代码中使用不合格的语句。
回答 4
如果我确实想从线程中退出程序怎么办(不是我真正想要的,但据我所知)?
我的首选方法是传递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