python - Python 3.7 和 3.8 之间 Python thread.join() 的区别
问题描述
我有一个小的 Python 程序,它在 Python 3.7 和 Python 3.8 中的行为不同。我很难理解为什么。Python 3.8的#threading 更改日志没有解释这一点。
这是代码:
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()
叫。从3.7的守护进程文档中:
当没有活着的非守护线程时,整个 Python 程序退出。
但显然线程应该还活着。
当我在 Python 3.8.6 (Arch) 中运行它时,我得到了预期的行为。也就是说,程序继续运行:
python test.py
main done.
hi
hi
hi
hi
...
3.8的守护进程文档声明与 3.7 相同:除非所有非守护线程都已加入,否则程序不应退出。
有人可以帮我理解发生了什么吗?
解决方案
_shutdown()
从 Python 版本 3.7.3 到 3.7.4的线程行为有一个未记录的变化。
我是这样找到它的:
为了追踪这个问题,我首先使用了inspect包来找出join()
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()
。
来自Python 3.7.3的CPython 源代码_shutdown()
,第 1279-1282 行:
t = _pickSomeNonDaemonThread()
while t:
t.join()
t = _pickSomeNonDaemonThread()
退出join()
时,该代码会在所有非守护线程上调用!MainThread
为了验证这些发现,我从源代码构建了 Python 3.7.4。它的行为确实不同。它使线程按预期运行,并且join()
不调用该函数。
这显然没有记录在Python 3.7.4 的发行说明和Python 3.8的变更日志中。
- 编辑:
正如 MisterMiyagi 在评论中指出的那样,有人可能会争辩说,扩展该join()
功能并将其用于发出终止信号并不是join()
. 恕我直言,这取决于口味。但是,应该记录在 Python 3.7.3 和之前的版本中,join()
由 Python 运行时在系统退出时调用,而对3.7.4
这种情况的更改不再是这种情况。如果记录得当,它将从一开始就解释这种行为。
推荐阅读
- excel - Excel:将“12.50 美元”数据粘贴到工作表中 -> 自定义数字格式可以将其视为粘贴操作中的数字吗?
- flutter - 如何在 Flutter 中将项目添加到自定义列表?
- sqlite - QSqlTableModel::removeRow() 无法设置 QSqlTableModel::OnManualSubmit
- c++ - 无法找到数组值的最小值和最大值 - C++
- node.js - 不能要求剧作家
- java - Hibernate 生成无效查询
- reactjs - React-Redux Hooks,将 inputValue 设置为空后,不会在 antD Input 中重新渲染
- docker - 在 docker-compose 中使用芹菜花和 auth
- python - 如何只打印一行重复数据中的一列?
- amazon-web-services - 有没有办法使用 CLI 或 GUI 一次性删除多个 AWS IAM 客户托管策略?