Best practices with QRunnable
-
We've heard many times why we shouldn't subclass QThread (http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong )
What about QRunnable? It's designed to be subclassed like how QThread used to be. It's somewhat safer since people can't add slots to it, but the flipside is that it can't emit signals to notify the world that it has finished.
The only example I could find that shows QRunnable in action is http://doc-snapshot.qt-project.org/5.0/thread-basics.html#example-1-using-the-thread-pool , but it's a trivial example. Does anyone know of any useful examples?
What are some recommended ways to return a result from QRunnable?
-
Well I would not use QRunnable. Maybe you should take a look at "QtConcurrent":http://qt-project.org/doc/qt-4.8/qtconcurrent.html which provide a future API that allows you to retrieve a result from an asynchronous computation. You can also watch the future with a QFutureWatcher which emits a signal when the computation is done.
-
Where do you get the idea that "the flipside is that it can’t emit signals to notify the world that it has finished"?
Here's an example I've used that features a subclass of QRunnable and QObject to create some user task that should be run threaded:
class tileTask : public QObject, public QRunnable { Q_OBJECT public: ... void run() { // do the task work here ... // say we're done emit finished(); } signals: void finished(); ... }; Then to thread the operations, use QThreadPool: void myThreadingExample() { QQueue<tileTask *> jobs; // Here we take some data e.g. an image that has been tiled // and stuff the tiles into a queue. int numTasks = maxx * maxy; for (int x=0; x < maxx; ++x) { for (int y=0; y < maxy; ++y) { tileTask *task = new tileTask(imageData(x, y)); jobs.enqueue(task); task->setAutoDeletet(true); } } QThreadPool *threadPool = QThreadPool::globalInstance(); for (int i=0; i < numTasks; ++i) { tileTask *newTask = jobs.dequeue() threadPool->start(newTask) QObject::connect(newTask, SIGNAL(finished()), this, SLOT(threadFinished()), Qt::QueuedConnection); } // Wait for all threads to finish threadpool->waitForDone(); }
The threadFinished() slot called by the task on completion just updated a progress bar and called qApp->processEvents() just to keep the gui updated.
QtConcurrent() may well be a neater method of achieving this, but I've not looked at it closely yet.
-
Thanks for your replies, rcari and KeithS.
I asked because I'm updating Qt's multithreading documentation, and would like to find out how people use the different thread-related technologies (QThread, QRunnable and QtConcurrent). I'm also interested in learning about the strengths+weaknesses of each. There's currently a little debate over whether QtConcurrent should be re-written.
[quote author="rcari" date="1353260562"]Well I would not use QRunnable[/quote]Would you mind sharing why you'd avoid QRunnable? Did you encounter problems with it, or is it just that QtConcurrent serves your purposes already?
[quote author="KeithS" date="1353321819"]Where do you get the idea that "the flipside is that it can’t emit signals to notify the world that it has finished"?
Here's an example I've used that features a subclass of QRunnable and QObject to create some user task that should be run threaded[/quote]Ah, of course; I forgot about multiple inheritance. Thank you for your insightful example.
This approach makes QRunnable more flexible -- like QThread, but with the added benefit of auto-cleanup and management by QThreadPool. I wonder if popularizing this approach will lead to the same issues that plagued QThread before, though.
-
Well, QRunnable is fine, but it does not take away the burden of actually running that thing. With QtConcurrent, you call a method, asynchronously. It's just a function call in a pretty wrapper. I don't have to care about thread creation, termination or pools... Everything is managed for me. Plus, since it is a simple function call, it usually avoids some of the pitfalls of concurrency when used in a pure functional way. I give it const input data and I get the result asynchronously: perfect.
However, I think QtConcurrent lacks the ability to customize the QFuture. For example, there is no way to deliver progress information from the @QtConcurrent::run@ as this is only reserved to the higher level filter/map/reduce APIs provided by Qt. In a similar fashion, many custom computations could benefit from the @QFuture::cancel()@ call. This would make it the perfect solution for doing asynchronous work. -
I see nothing wrong with QRunnable. That's not to say I see no value on QtConcurrent - far from it, the more the complexities of thread programming can be managed at a higher level the better, as we are all going to be embracing multithreading more and more in the future. However, as rcari points out, QtConcurrent isn't all there yet. It's nice it works on QList, QLinkedList, QVector, but in practise I might just want to run on some specific data structure which is doesn't support.
-
Well perhaps the best of both worlds would be to have the ability to push a QRunnable to QtConcurrent and to have additional methods on that QRunnable to provide progress information and cancellation control eg:
@
void setProgress( qint64 progress, qint64 maxProgress );
bool isCanceled() const;
@
This is kind of what I did on a personal projet with what I called a "Tasklet":https://github.com/rcari/libKore/blob/master/source/src/parallel/Tasklet.cpp. -
I think there is value in having both low-level (QRunnable) and high-level (QtConcurrent) APIs, and letting them communicate with each other. That would cover the need for easy-to-use and robust concurrency, as well as the ability to fine-tune performance, as well as everything in between.
I've mainly used worker objects with QThread, as I find that it fits well with Qt's signal-slot mechanism. I've recently learnt that it's not as scalable as using a thread pool though. Have either of you used QThread much in your work? (rcari, I notice that you use QThread to determine the active thread; do you also use it to manage your tasks?)
By the way, I forgot to provide links in my last post. If you're interested in weighing in on the discussion on QtConcurrent's future, have a look at the Development mailing list (they're currently thinking of introducing KDE's ThreadWeaver or Intel's Thread Building Blocks into Qt):
http://lists.qt-project.org/pipermail/development/2012-November/007921.html
http://lists.qt-project.org/pipermail/development/2012-November/007952.html
http://lists.qt-project.org/pipermail/development/2012-November/007901.html -
As for my experience subclassing QThread works well while we start event loop with exec in run and triggering signals/slots outside. I haven't tested yet new approach with QThread as manager for other class based on QObject.
-
There's no argument that subclassing QThread can work -- and indeed it was the only way to use QThread in earlier versions of Qt -- but since that method is rife with potential problems, that particular methodology is deprecated for most common applications and should be avoided if possible.