为什么延迟会改变 运行 QThreads 的数量?

Why does a delay change the number of running QThreads?

以下示例程序将 QThread 个实例用于队列中的 运行 个作业。

from queue import Queue
from sys import argv
from threading import Lock
from time import sleep
from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication

class Worker(QObject):
    finished = pyqtSignal()

    def __init__(self, number):
        super().__init__()
        self.number = number

    @pyqtSlot()
    def work(self):
        while True:
            job = queue.get()
            if job is None:
                self.finished.emit()
                return
            with lock:
                print('worker={} job={}'.format(self.number, job))
            sleep(1)

app = QApplication(argv)
lock = Lock()
queue = Queue()
threads = []
nthreads = 4
for ithread in range(nthreads):
    thread = QThread()
    worker = Worker(ithread + 1)
    worker.moveToThread(thread)
    thread.started.connect(worker.work)
    worker.finished.connect(thread.quit)
    thread.start()
    threads += [thread]
    #-----------
    sleep(1e-10)
    #-----------

for ijob in range(10):
    queue.put(ijob + 1)

for _ in range(nthreads):
    queue.put(None)
for thread in threads:
    thread.wait()

通过 sleep 调用(见标记线),所有线程都按预期 运行ning。输出:

worker=1 job=1
worker=2 job=2
worker=3 job=3
worker=4 job=4
[…]

没有调用,任意数量的线程是 运行ning。输出:

worker=4 job=1
worker=4 job=2
worker=4 job=3
worker=4 job=4
[…]

我已经在 Python 3.6.2、其他版本、可变睡眠持续时间、不同的语句顺序和 运行ning 事件循环中用 PyQt 5 测试了这个。

为什么 sleep 调用会改变 运行ning 线程的数量?

前三个工作对象容易被垃圾回收,而第四个作为全局变量保留。 sleep 只允许每个线程有足够的时间调用其关联工作程序的 work 方法,这将使它们保持活动状态(因为方法 运行 阻塞了 while 循环)。如果没有 sleep,前三个工作人员将立即被垃圾收集,只留下第四个处理队列。如果工人被保存在一个列表中(与线程相同),您应该看到 所有 正在使用它们(有或没有睡眠)。

为了证明这是怎么回事,您可以将其添加到 Worker class:

class Worker(QObject)
    ...

    def __del__(self):
        print('deleted:', self.number)