python - Python 3.7 和 3.8 之间 Python thread.join() 的差异

标签 python multithreading

我有一个在 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/

相关文章:

python - 如何从递归函数返回单个 bool 值?

python - Django:如果相关对象不存在,则从 OneToOneField 返回 'None'?

c++ - 从 Boost 线程返回 Double

java - 在某个线程中的 jaBehaviorSubject 上运行 doOnNex

c# - 带有 SerialPort 的 Windows 窗体 - 应用程序在关闭窗体后挂起

android - 应用程序关闭时线程是否会停止?

c++ - 我如何为以下代码进行进程同步?

python - 生成内置类型值的字典理解

python - django 用一个提交按钮提交两种不同的表单

python - 如何在 IDLE 中重新加载 Python 模块?