我有一个在 Python 3.7 和 Python 3.8 中表现不同的小型 Python 程序。我正在努力理解为什么。 #threading changelog对于 Python 3.8 没有解释这一点。
这是代码:
import time
from threading import Event, Thread
class StoppableWorker(Thread):
def __init__(self):
super(StoppableWorker, self).__init__()
self.daemon = False
self._stop_event = Event()
def join(self, *args, **kwargs):
self._stop_event.set()
print("join called")
super(StoppableWorker, self).join(*args, **kwargs)
def run(self):
while not self._stop_event.is_set():
time.sleep(1)
print("hi")
if __name__ == "__main__":
t = StoppableWorker()
t.start()
print("main done.")
当我在 Python 3.7.3 (Debian Buster) 中运行它时,我看到以下输出:python test.py
main done.
join called
hi
程序自行退出。不知道为什么join()
叫做。来自 daemon documentation 3.7:
The entire Python program exits when no alive non-daemon threads are left.
但显然线程应该还活着。
当我在 Python 3.8.6 (Arch) 中运行它时,我得到了预期的行为。也就是说,程序一直在运行:
python test.py
main done.
hi
hi
hi
hi
...
daemon documentation对于 3.8 状态与 3.7 相同:除非所有非守护线程都已加入,否则程序不应退出。有人可以帮我理解发生了什么吗?
最佳答案
线程行为 _shutdown()
存在未记录的更改从 Python 版本 3.7.3 到 3.7.4。
这是我找到它的方法:
为了追踪这个问题,我首先使用了 inspect包找谁join()
s Python 3.7.3 运行时中的线程。我修改了join()
获得一些输出的函数:
...
def join(self, *args, **kwargs):
self._stop_event.set()
c = threading.current_thread()
print(f"join called from thread {c}")
print(f"calling function: {inspect.stack()[1][3]}")
super(StoppableWorker, self).join(*args, **kwargs)
...
使用 Python 3.7.3 执行时,会打印:main done.
join called from thread <_MainThread(MainThread, stopped 139660844881728)>
calling function: _shutdown
hi
所以MainThread
已停止,调用 join()
方法。 MainThread
中负责的函数是 _shutdown()
.来自 CPython source用于 Python 3.7.3 用于
_shutdown()
,第 1279-1282 行: t = _pickSomeNonDaemonThread()
while t:
t.join()
t = _pickSomeNonDaemonThread()
该代码调用 join()
当 MainThread
在所有非守护线程上运行时退出!该实现是 changed in Python 3.7.4 .
为了验证这些发现,我从源代码构建了 Python 3.7.4。它的行为确实不同。它使线程按预期运行,
join()
不调用函数。这显然没有记录在 release notes of Python 3.7.4 中。也不在 changelog of Python 3.8 .
- 编辑:
正如 MisterMiyagi 在评论中指出的那样,人们可能会争辩说,扩展
join()
函数并将其用于信令终止不是 join()
的正确用法.恕我直言,这取决于口味。但是,应该记录在 Python 3.7.3 及之前版本中,join()
在系统退出时由 Python 运行时调用,同时更改为 3.7.4
这已不再是这种情况。如果正确记录,它将从一开始就解释这种行为。
关于python - Python 3.7 和 3.8 之间 Python thread.join() 的差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64917285/