Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

What's the correct way of using QThread?



  • Hi,

    I'm starting a new project that basically consists of a set of worker classes (a parser, a socket manager, etc) and a GUI.

    The GUI has to be fluid. I want the main thread to contain only the GUI. And a separate thread that holds the worker classes.

    I wonder what's the correct way of doing this, since Qt Docs are a little fuzzy regarding QThreads. QtDocs suggest subclassing QThread and reimplementing run(). But on the other hand, a while ago I've read a blog post saying that the correct way of using QThreads is calling the moveToThread method. Unfortunately I can't seem to find that blog post again :(

    Currently I have this:

    @
    #include <QtGui/QApplication>
    #include "MainWindow.h"
    #include <QtDebug>

    #include <QThread>

    #include "CAbstractParser.h"

    int main(int argc, char *argv[])
    {

    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    
    cLog->notice("==========================================");
    cLog->notice("STARTING UP...");
    cLog->notice("==========================================");
    
    QThread workThread(qApp);
    
    CAbstractParser parser("C:/Users/User/Desktop/test.log");
    parser.moveToThread(&workThread);
    parser.connect(&workThread, SIGNAL(started()), SLOT(start()));
    parser.connect(&workThread, SIGNAL(finished()), SLOT(stop()));
    
    workThread.connect(qApp, SIGNAL(aboutToQuit()), SLOT(quit()));
    workThread.start();
    
    return a.exec&#40;&#41;;
    

    }
    @

    CLogger is a Singleton. It has a CLogger::instance() method to retreive the singleton class. cLog is a macro meaning CLogger::instance(). ( Just like qApp and QApplication::instance() )

    MainWindow will connect to a SIGNAL of cLog on constructor, so this is where CLogger's instance is created.

    CAbstractParser starts a timer with start(), and kills it with stop(). Every 1000ms, parser will 'parse' new lines of a specified log file.
    Sometimes CAbstractParser will log something with cLog->notice("Something")

    I'm getting a warning when closing the program and I don't know why or how to remove it:
    @QObject::killTimers: timers cannot be stopped from another thread@



  • [quote author="ivan.todorovich" date="1298994374"]

    I'm getting a warning when closing the program and I don't know why or how to remove it:
    @QObject::killTimers: timers cannot be stopped from another thread@

    [/quote]

    Ok, I've found the problem:
    @
    parser.connect(&workThread, SIGNAL(started()), SLOT(start()));
    parser.connect(&workThread, SIGNAL(finished()), SLOT(stop()));
    @
    .. should be..
    @
    parser.connect(&workThread, SIGNAL(started()), SLOT(start()), Qt::QueuedConnection);
    parser.connect(&workThread, SIGNAL(finished()), SLOT(stop()), Qt::QueuedConnection);
    @
    Apparently, I have to explicitly specify the QueuedConnection, contrary to what the documentation says:
    [quote]
    Qt::AutoConnection (default) Same as DirectConnection, if the emitter and receiver are in the same thread. Same as QueuedConnection, if the emitter and receiver are in different threads
    [/quote]

    Maybe this is a bug? I'm using 4.7.1

    Anyway, I'm still wondering what's the correct way of using QThreads.



  • That doc line is wrong. It has been fixed in the latest snapshot, though.

    See also what I wrote in my article http://developer.qt.nokia.com/wiki/Threads_Events_QObjects#913fb94dd61f1a62fc809f8d842c3afa



  • [quote author="peppe" date="1298995839"]That doc line is wrong. It has been fixed in the latest snapshot, though.

    See also what I wrote in my article http://developer.qt.nokia.com/wiki/Threads_Events_QObjects#913fb94dd61f1a62fc809f8d842c3afa[/quote]

    So I'm doing the things the right way.

    --

    I have another question though. Isn't there any way I could do something like this: (pseudo Qt code)

    @
    #include <QtGui/QApplication>
    #include "MainWindow.h"
    #include <QtDebug>

    #include <QThread>

    #include "CAbstractParser.h"

    int main(int argc, char *argv[])
    {

    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    
    cLog->notice("==========================================");
    cLog->notice("STARTING UP...");
    cLog->notice("==========================================");
    
    QThread::run(work);
    
    return a.exec&#40;&#41;;
    

    }

    int work()
    {

    QThread t;
    
    // heavy startup [...]
    
    CAbstractParser parser("C:/Users/User/Desktop/test.log");
    parser.start();
    
    // AnotherWorkerClass work1;
    // work1.start();
    
    // AnotherWorkerClass work2;
    // work2.start();
    
    // ...
    
    return t.exec(&#41;;
    

    }
    @

    I know this isn't possible, but hopefully you'll get the idea.



  • A good article on using threads in 4.4 is found here "How To Really, Truly Use QThreads; The Full Explanation":http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/ This helped me when I read it. I had been running around in circles like a headless chicken up till then.



  • Hello. I think you can do like so:
    workerthread.hpp
    @//#your includes, and QThread
    class WorkerThread: public QThread
    {
    Q_OBJECT
    public:
    WorkerThread(/a pointer to an object
    that signals to worker
    /Caller* c)
    : m_Caller(c) // remember pointer to use in threadfunction
    { this->start(); } // start automatically, so you should only create an object
    ~WorkerThread()
    {
    this->exit(0); // ask main loop to quit
    this->wait(3000); // wait for thread to finish
    if(this->isRunning()) // if it failed to finish within 3 sec
    this->terminate(); // kill it with fire
    }

    void run() // yup the threadfunction by ourselves
    {
    m_Worker = new Worker(); // it should be created in threadfunction!
    connect(m_Caller, SIGNAL(...), m_Worker, SLOT(...),
    Qt::QueuedConnection);
    exec(); /* here event processing loop starts, as it is for QThread by default, but the difference we have objects connected */
    delete m_Worker; // works only after exit() call when main loop ends
    }

    private:
    Worker* m_Worker; // pointer to worker object
    Caller *m_Caller; // pointer to caller object
    }@

    You should remember that QThread object itself is not a thread, but only handler and it exists in parent thread.
    Also if you have strong thread-sync requirement, you can apply some sync objects like QSemaphore to make sure that objects are connected when you first time signal an event (is simple, i can show if needed).



  • The general opinion now seems to be that you should not be extending QThread but write all your code in a worker object then pass it to a thread using the moveToThread method. These two articles are the best that I have found are a blog by Bradley Hughes "You're doing it wrong":http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/ and this which I think explains it betterb"How To Really, Truly Use QThreads; The Full Explanation":http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/.


  • Moderators

    Hi all, I've formally opened a ticket for this old old issue in https://bugreports.qt-project.org/browse/QTBUG-26899

    I just found out that the community can submit patches for the Qt docs too. Perhaps we can collaboratively write up a better example for QThread usage?



  • [quote author="JKSH" date="1345632260"]Hi all, I've formally opened a ticket for this old old issue in https://bugreports.qt-project.org/browse/QTBUG-26899

    I just found out that the community can submit patches for the Qt docs too. Perhaps we can collaboratively write up a better example for QThread usage?[/quote]

    That would be nice. So much is representative of the former way.


Log in to reply