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();
}
@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();
}
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();
}
@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/.
-
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.