Mir ist das zu viel gewurschtel. Die von mir und __blackjack__ angemerkten Vorteile eines einzelnen Worker-Threads beziehen sich darauf, dass es nichts bringt, beliebig viele Threads hochzufeuern, die sich dann einfach nur gegenseitig auf die Fuesse treten.
Hier habe ich mal ein kleines Test-Skript zusammengeworfen, das die von mir beschriebene Semantik hat. Wenn man auf einen Knopf drueckt, bekommt der wartende Hintergrund-Thread einfach seinen work-Slot aufgerufen.
Beim anderen Button wird - so wie bei dir in urspruenglichen Code den wir damals diskutiert haben - jeweils ein neuer QThread gestartet. Egal welche Methode man verwendet, es braucht beides mal auf meinem System ca. 6 Sekunden die Items in das Tree-Widgets zu stecken. Womit beide Ansaetze keinen Performance-Einbussen haben. Die ich mir auch nicht erklaeren koennte.
Warum dein Code nun so ein komisches Verhalten zeigt - ja nun. Wie gesagt, das ist mir zu schwurbelig.
Code: Alles auswählen
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
import time
import threading
from PyQt5.QtCore import QTimer, QObject, pyqtSignal, \
     QThread, Qt
from PyQt5.QtWidgets import (
    QApplication,
    QWidget,
    QLabel,
    QTreeWidget,
    QTreeWidgetItem,
    QPushButton,
    QVBoxLayout,
)
TASKNUMBER = 460
class Worker(QObject):
    generated_item = pyqtSignal(str)
    work_started = pyqtSignal()
    work_done = pyqtSignal()
    def work(self):
        self.work_started.emit()
        for i in range(TASKNUMBER):
            for _ in range(1000000):  # just burn some time
                pass
            self.generated_item.emit("Item {}".format(i))
        self.work_done.emit()
class TimeMeasurer(QObject):
    def start(self):
        self._start = time.monotonic()
    def stop(self):
        elapsed = time.monotonic() - self._start
        print("Time elapsed:", elapsed)
class Forwarder(QObject):
    """
    Just a holder for one signal
    """
    enqueue_work = pyqtSignal()
class BatchCounter(QObject):
    count_changed = pyqtSignal(int)
    def __init__(self):
        super().__init__()
        self._lock = threading.Lock()
        self._count = 0
    def inc(self):
        with self._lock:
            self._count += 1
        self.count_changed.emit(self._count)
    def dec(self):
        with self._lock:
            self._count -= 1
        self.count_changed.emit(self._count)
def main():
    forwarder = Forwarder()
    counter = BatchCounter()
    extra_threads = []
    def generate_work_load():
        counter.inc()
        forwarder.enqueue_work.emit()
    def generate_work_load_in_extra_thread():
        def shutdown_thread():
            print("running threads:", len(extra_threads))
            extra_thread.quit()
            extra_thread.wait()
            extra_threads.remove(extra_thread)
            print("shutdown_thread")
        extra_thread = QThread()
        worker = extra_thread.worker = Worker()
        worker.moveToThread(worker_thread)
        extra_thread.started.connect(worker.work)
        tm = extra_thread.tm = TimeMeasurer()
        worker.generated_item.connect(add_item)
        worker.work_started.connect(tm.start)
        worker.work_done.connect(tm.stop)
        worker.work_done.connect(counter.dec)
        worker.work_done.connect(shutdown_thread)
        extra_threads.append(extra_thread)
        counter.inc()
        extra_thread.start()
    app = QApplication(sys.argv)
    worker_thread = QThread()
    worker = Worker()
    worker.moveToThread(worker_thread)
    w = QWidget()
    w.setWindowTitle('ThreadTest')
    layout = QVBoxLayout(w)
    start_in_worker_thread_button = QPushButton(w)
    start_in_worker_thread_button.setText("Add Items in Worker Thread")
    start_in_worker_thread_button.clicked.connect(generate_work_load)
    start_in_extra_thread_button = QPushButton(w)
    start_in_extra_thread_button.setText("Add Items in Extra Thread")
    start_in_extra_thread_button.clicked.connect(generate_work_load_in_extra_thread)
    counting_label = QLabel(w)
    counter.count_changed.connect(lambda count: counting_label.setText("Batches: {}".format(count)))
    tree = QTreeWidget(w)
    layout.addWidget(start_in_worker_thread_button)
    layout.addWidget(start_in_extra_thread_button)
    layout.addWidget(counting_label)
    layout.addWidget(tree)
    def add_item(text):
        parent = QTreeWidgetItem(tree)
        tree.addTopLevelItem(parent)
        parent.setText(0, text)
    w.show()
    tm = TimeMeasurer()
    worker.generated_item.connect(add_item)
    worker.work_started.connect(tm.start)
    worker.work_done.connect(tm.stop)
    worker.work_done.connect(counter.dec)
    forwarder.enqueue_work.connect(worker.work)
    # the ONE background thread
    worker_thread.start()
    app.exec_()
    worker_thread.quit()
    worker_thread.wait()
    sys.exit()
if __name__ == '__main__':
    main()
