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
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.