Почему задержка меняет количество запущенных 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
вызов (см. отмеченную строку), все потоки работают как положено. Выход:
worker=1 job=1
worker=2 job=2
worker=3 job=3
worker=4 job=4
[…]
Без вызова выполняется произвольное количество потоков. Выход:
worker=4 job=1
worker=4 job=2
worker=4 job=3
worker=4 job=4
[…]
Я проверил это с PyQt 5 в Python 3.6.2, другими версиями, переменной продолжительностью сна, различными порядками операторов и циклом запуска событий.
Почему sleep
позвонить изменить количество запущенных потоков?
1 ответ
Первые три рабочих объекта склонны к сборке мусора, а четвертый остается глобальной переменной. sleep
позволяет каждому потоку достаточно времени для вызова work
метод связанного с ним работника, и это сохранит их всех живыми (так как методы запускают циклы while). Без sleep
первые три работника будут немедленно собраны, оставив только четвертого для обработки очереди. Если рабочие хранятся в списке (так же, как потоки), вы должны увидеть, что все они используются (с или без сна).
Чтобы доказать, что это происходит, вы можете добавить это к Worker
учебный класс:
class Worker(QObject)
...
def __del__(self):
print('deleted:', self.number)