Mr.thread
Mr.thread

Reputation: 91

PySide: Threads destroyed whie still running

I'm having problem with QThreads in python. I would like to start my multi QThread when I push on button Run.

But the compiler outputs following error: "QThread: Destroyed while thread is still running"

I don't know what is wrong with my code. Any help would be appreciated. Here is my code:

# -*- coding: utf-8 -*-
from PySide import QtCore, QtGui
from Ui_MainWindow import Ui_MainWindow
from queue import Queue
import sys, re, random
import time, random, re, urllib.request
from urllib.parse import urljoin

class Worker(QtCore.QThread):
    def __init__(self,threadID, name, q, delay):
        QtCore.QThread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.q = q
        self.delay = delay
        self._running = False

    def run(self):
        self._running = True
        print ("start - %s" %self.name)
        while self._running:
            req = self.request(self.name,  self.q, self.delay)

    def stop(self, wait=False):
        print (self.name)
        self._running = False


    def request(self, threadName, q1, delay):
        while not self.q.empty():
            time.sleep(delay)
            q = q1.get()
            print ("%s: %s %s %s" % (threadName, time.ctime(time.time()), q, delay))
        if self.q.empty():
            print ("queue empty")
            self.stop()

class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        QtGui.QMainWindow.__init__(self, parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.backend = Queue()
        self.connect(self.ui.actionStart, QtCore.SIGNAL('triggered()'),self.start)

    def start(self):
        try :
            f1 = open('./payload/backend.log')
        except FileNotFoundError as e:
            return

        threadList = ["Thread-1", "Thread-2", "Thread-3", "Thread-4", "Thread-5"]
        self.url = "http://test.com"
        self.threads = []
        threadID = 1

        for payload in f1.read().splitlines() :
            full_url = urljoin(self.url, payload)
            self.backend_dir.put(full_url)


        for tName in threadList:
            ran_int = random.randint(1, 2)
            downloader = Worker(threadID, tName, self.backend, ran_int)
            downloader.start()
            self.threads.append(downloader)


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_()) 

log

QThread: Destroyed while thread is still running
QThread: Destroyed while thread is still running
QThread: Destroyed while thread is still running
QThread: Destroyed while thread is still running

Upvotes: 0

Views: 1105

Answers (3)

ekhumoro
ekhumoro

Reputation: 120588

The problem is caused by the way you are using the queue.

All the threads start and begin their tasks normally, up until the queue becomes empty. At that point, the first thread to finish terminates, but the other four threads are left waiting for an item to be returned from the queue, which never comes.

This is because you use get with no arguments, which will block indefinitely until an item becomes available. Instead, you should use get_nowait, and also call stop() unconditionally at the end of request():

from queue import Queue, Empty
...

class Worker(QtCore.QThread):
     ...

    def request(self, threadName, q1, delay):
        while not q1.empty():
            time.sleep(delay)
            try:
                q = q1.get_nowait()
            except Empty:
                break
            else:
                print ("%s: %s %s %s" % (threadName, time.ctime(time.time()), q, delay))
        print ("queue empty")
        self.stop()

Upvotes: 0

nb1987
nb1987

Reputation: 1410

I believe that you need to call self.threads.append(downloader) before downloader.start() so that the thread doesn't go out of scope and get garbage collected.

Upvotes: 0

Plouff
Plouff

Reputation: 3470

  1. You are trying to do something that is a built-in of Qt: a QThreadPool.

I would advice you to read the doc and use it instead.

  1. If you really want to use QThread: You should not subclass QThread. Instead you should subclass the basic QObject to create your worker and use the moveToThread method:

    class WorkerSignals(QObject):
        finished = pyqtSignal()
    
    class Worker(QObject):
        def __init__():
            self.signal = WorkerSignals()
    
            def run(self):
            # Your stuff
            print('running')
            self.signal.finished.emit()
    

Then somewhere else:

    thread = QThread()
    worker = Worker(...)
    worker.moveToThread(thread)
    thread.started.connect(worker.run)
    worker.finished.connect(thread.quit)
    worker.finished.connect(worker.deleteLater)
    thread.finished(thread.deleteLater)
    thread.start()

The solution is a rough translation of this one in C++: https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/

Hope this helps!

Upvotes: 1

Related Questions