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

2024-04-26 07:23:24 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个小Python程序,在Python3.7和Python3.8中表现不同。我很难理解为什么。Python 3.8的#threading changelog没有解释这一点

代码如下:

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的daemon documentation开始:

The entire Python program exits when no alive non-daemon threads are left.

但很明显,这根线应该还活着

当我在Python3.8.6(Arch)中运行这个程序时,我得到了预期的行为。也就是说,程序将继续运行:

python test.py
main done.
hi
hi
hi
hi
...

3.8的daemon documentation状态与3.7相同:除非所有非守护进程线程都已加入,否则程序不应退出

有人能帮我理解发生了什么事吗


Tags: importself程序eventtimemaindefhi
2条回答

“新功能”仅列出新功能。在我看来,这些变化就像一个bug修复。 https://docs.python.org/3.7/whatsnew/3.7.html在顶部附近有一个changelog链接。根据@Felix答案中的研究,我们应该看看3.7.4中发布的错误修复。 https://docs.python.org/3.7/whatsnew/changelog.html#python-3-7-4-release-candidate-1

这可能就是问题所在:https://bugs.python.org/issue36402 bpo-36402:修复Python关闭时等待线程时的争用条件。等待所有非守护进程线程的Python线程状态被删除(加入所有非守护进程线程),而不仅仅是等待非守护进程Python线程完成

线程_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()

CPython sourcefor Python 3.7.3 for _shutdown(),第1279-1282行:

    t = _pickSomeNonDaemonThread()
    while t:
        t.join()
        t = _pickSomeNonDaemonThread()

MainThread退出时,该代码在所有非守护进程线程上调用join()

这一执行是changed in Python 3.7.4

为了验证这些发现,我从源代码构建了Python3.7.4。它的行为确实不同。它保持线程按预期运行,并且不调用join()函数

这显然没有记录在release notes of Python 3.7.4changelog of Python 3.8

编辑:

正如Mistermiagi在评论中指出的那样,有人可能会认为扩展join()函数并将其用于信令终止不是join()的正确使用。依我看,那要看口味了。但是,应该记录在Python3.7.3及之前的版本中,Python运行时在系统退出时调用join(),而更改为3.7.4后,情况就不再是这样了。如果有适当的记录,它将从一开始就解释这种行为

相关问题 更多 >