Lock Free Multithreading in Qt
If multithreading is challenging to get right in your applications, then lock-free multithreading is down-right killer.
This article won’t go into detail about lock-free algorithms, but instead I will offer a “poor man’s” method for crossing thread boundaries in Qt without using locks (no mutexes, no semaphores). At least, your code won’t have any locks. More on that later.
For years, Qt has sported an easy-to-use threading library, based around a class called QThread. As of Qt4, you can use QThread to start your own event loops. This might sound somewhat uninteresting at first, but it means you can have your own signals and slots outside the main thread. The Trolls created a new way to connect signals to slots such that signals can actually cross thread boundaries. I can now emit a signal in one thread and receive it in a slot in a different thread. This is hugely useful when you want to, for example, integrate a blocking-happy library into your application. Here’s what I’m talking about in pictures:
Signals can arrive at any time from the threads, just like any other signal, and the code in the main event loop doesn’t know anything about multi-threading, locks, or condition variables.
An Example
Let’s say you want to integrate with a third-party library that blocks when you call its functions. Of course, if you call this library from your main event loop, your application will freeze while it’s running. That would be annoying, and you can give your users a better experience than that. Let’s wrap that library in a QThread. First, you need to declare a new QThread subclass, like this:
class MyLibraryWrapper : public QThread { Q_OBJECT public: MyLibraryWrapper(); protected: void run(); signals: void done(const QString &results); private slots: void doTheWork(); };
The run() method is called automagically by Qt when the caller calls start() on your thread. This is similar to how Java’s Thread class works.
The done() signal is how the object tells you it is finished with its work. It emits a QString in this example, but it can emit anything you want (note that if you want to emit non-primitive or non-Qt types, you need to use the Q_DECLARE_METATYPE() macro, which we won’t go into in this article).
The doTheWork() slot is there to actually do the blocking work. It has to be a slot so we can put it to work after our event loop starts up (which you’ll see in a minute).
Now for the implementation of MyLibraryWrapper:
MyLibraryWrapper::MyLibraryWrapper() : QThread() { // We have to do this to make sure our thread has the // correct affinity. moveToThread(this); // This will do nothing until the user calls start(). } void MyLibraryWrapper::run() { // This schedules the doTheWork() function // to run just after our event loop starts up QTimer::singleShot(0, this, SLOT(doTheWork())); // This starts the event loop. Note that // exec() does not return until the // event loop is stopped. exec(); } void MyLibraryWrapper::doTheWork() { // Do the heavy-duty blocking stuff here // (simulated by a 5 second sleep for // this example) sleep(5); // When you're done, emit the results: emit done("First job's finished."); // And some more sleeping for fun sleep(3) emit done("Second job's finished."); // ... }
To actually use this new class, all you have to do is instantiate it in your main event loop and connect it to a slot. Here’s an example. Let’s say you have a QMainWindow called MyMainWindow. This is how you would set it up as a result of a user button click:
void MyMainWindow::on_someButton_clicked() { MyLibraryWrapper *wrapper = new MyLibraryWrapper(); // This is the magic that tells the wrapper to // notify us when it's done. We use a QueuedConnection // to make sure Qt delivers the signal in a thread // safe manner connect(wrapper, SIGNAL(done(QString)), this, SLOT(wrapperDone(QString)), Qt::QueuedConnection); // This kicks off the wrapper's event loop by causing its // run() method to be called wrapper->start(); } void MyMainWindow::wrapperDone(const QString &results) { // The wrapper is now done with its long, blocking // operation, and we didn't freeze the application. // Yay for us! qDebug() << "Here are your results:" << results; }
Conclusion
Notice that none of our code uses a semaphore, condition variable, or mutex? Using QThread makes it super easy to wrap up libraries that need to block the event loop in a way that is transparent to the caller.
I claimed earlier that this code would be “lock free”. I can’t actually claim that is 100% true. I don’t know for sure, but I imagine that Qt’s internal event loop code does indeed use locks to pass signals between event loops. All I know for sure is that this code crosses thread boundaries without any locks.
Stay tuned for another article that shows how you can call setter functions on your threaded objects without any need for locks.
16 comments to “Lock Free Multithreading in Qt”
What if you deal with a thread process that is continuous unless you specifically call it to either pause or stop. One other triggering event is a close event, if thread running, then stop it gracefully?
Sal, your question prompted me to write another article on how to stop QThreads. Here you go:
http://thesmithfam.org/blog/2010/02/07/talking-to-qt-threads/
I hope that answers your question.
Hi Dave, interesting article. Don’t know if your blog is meant for direct help but here goes.
I am porting an application from borland and a newby to Qt. I can’t get the emit to work. I have a main application with a widget, a button and a label (ui form), the on_button_clicked function declares and starts the thread “W” and each loop in the thread gives a result (an int) that has to be show on the label. so I have in the thread class:
signals:
void show(const int i);
in the widget class
void showit(const int i);
which is linked as:
connect(W, SIGNAL(show(int)),this, SLOT(showit(int)), Qt::QueuedConnection);
When I debug the prog the thread is started and it runs 100 loops, each loop emits the counter to showit. The thread runs but nothing is shown.
thanks.
My first question is: are you sure you need a thread? It sounds like a QTimer would suffice. It’s usually better to avoid threads if you can.
My other question is: what does the showit() method look like?
thanks for helping. Showit is nothing else but:
void mod6::showit(const int i)
{
label->setNum(i);
}
where mod6 is the interface class.
In the original prog the thread is a waterbalance model that can run for several hours and gives a lot of results back to the interface at each loop (values, a graph, digital maps). During the run the interface cannot block because the user may need to do things (zoom in and so on). I was also looking at the mandelbrot example in Qt but I liked your mutex-less example.
Victor, do you see any warnings on the command line when you run your app (on Windows, you need to add “CONFIG += console” to your .pro file).
I ask because sometimes if you have a typo in your connect() call with your SIGNAL and SLOT text, you won’t see an error in the compiler, but on the command line, you will see a warning when connect() is called.
Also, what is “W”? This should be your thread, not your widget, right?
Lastly, have you used qDebug()? Very handy. Just #include and then you can add debug output like this:
qDebug() << "showit called with value" << i; Let me know.
Oh, and a couple more things:
Is your method showit() declared as a “slot”, and not just “public”, like this:
public slots:
showit(int i);
Is the show() signal declared as a signal in your thread class like this:
signals:
show(int i);
Also, do you have Q_OBJECT in your Widget class declaration? (you need Q_OBJECT for any class that has signals or slots). You need Q_OBJECT in your thread class too.
If you add Q_OBJECT, you have to re-run “qmake” by the way.
–Dave
sorry! I did not declare sowit under “private slots:” in the class, still getting the hang of this. However now that it runs it does not show every step in the counter, only the last one. I guess emit is not meant for passing values. I’ll check what qtimer does
it works now, I had commented out “movetothread(this)”. Sorry for polluting your blog! ;-)
Okay, it turns out that subclassing QThread is not actually necessary:
http://blog.exys.org/entries/2010/QThread_affinity.html
You don’t have to write a run() method. You can just *have* a QThread in your class, and call start() on it. Then, call moveToThread(&thread) rather than moveToThread(this).
Very interesting.
http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/
moveToThread(this) is deprecated
Hi,
Thanks for the thread article,Iam using Q threads after reading this,I have 1 doubt regarding thread termination what I have to do to stop a thread if the thread is blocked(I have file I/O operation which takes around 4-5 minutes).I cannot start the event loop as the file read function is blocking the event loop in thread.Pls comment.
qtnewbie:
You could try using many small I/O operations (1-2 seconds) instead of one big one (4-5 minutes). You could also look into non-blocking I/O.
–Dave
Thanks for the article, Dave! Solved my problem. I am using the Chilkat socket library for my app, but can’t use the asynchronous socket calls because they aren’t supported on Linux. Wrapping things in a QThreat wrapper was the perfect answer!
Is QTimer::singleShot(0, this, SLOT(doTheWork())) safe to be called in a secondary thread? The function is reentrant but the class QTimer is not instantiated to a new instance in the secondary thread.
@anon:
moveToThread(this) isn’t deprecated as such. That article (reposted here since it’s moved to digia’s hub) simply points out that since Qt4.4 you don’t have to (and in his opinion, shouldn’t) subclass QThread anymore.
http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/
And the article Bradley T. Hughes refers to is now here:
http://blog.qt.digia.com/blog/2006/12/04/threading-without-the-headache/