Home > Articles > Programming > C/C++

  • Print
  • + Share This
This chapter is from the book

Using Qt's Classes in Secondary Threads

A function is said to be thread-safe when it can safely be called from different threads simultaneously. If two thread-safe functions are called concurrently from different threads on the same shared data, the result is always defined. By extension, a class is said to be thread-safe when all of its functions can be called from different threads simultaneously without interfering with each other, even when operating on the same object.

Qt's thread-safe classes include QMutex, QMutexLocker, QReadWriteLock, QReadLocker, QWriteLocker, QSemaphore, QThreadStorage<T>, and QWaitCondition. In addition, parts of the QThread API and several other functions are thread-safe, notably QObject::connect(), QObject::disconnect(), QCoreApplication::postEvent(), and QCoreApplication::removePostedEvents().

Most of Qt's non-GUI classes meet a less stringent requirement: They are reentrant. A class is reentrant if different instances of the class can be used simultaneously in different threads. However, accessing the same reentrant object in multiple threads simultaneously is not safe, and such accesses should be protected with a mutex. Reentrant classes are marked as such in the Qt reference documentation. Typically, any C++ class that doesn't reference global or otherwise shared data is reentrant.

QObject is reentrant, but there are three constraints to keep in mind:

  • Child QObjects must be created in their parent's thread.

    In particular, this means that the objects created in a secondary thread must never be created with the QThread object as their parent, because that object was created in another thread (either the main thread or a different secondary thread).

  • We must delete all QObjects created in a secondary thread before deleting the corresponding QThread object.

    This can be done by creating the objects on the stack in QThread::run().

  • QObjects must be deleted in the thread that created them.

    If we need to delete a QObject that exists in a different thread, we must call the thread-safe QObject::deleteLater() function instead, which posts a "deferred delete" event.

Non-GUI QObject subclasses such as QTimer, QProcess, and the network classes are reentrant. We can use them in any thread, as long as the thread has an event loop. For secondary threads, the event loop is started by calling QThread::exec() or by convenience functions such as QProcess::waitForFinished() and QAbstractSocket::waitForDisconnected().

Because of limitations inherited from the low-level libraries on which Qt's GUI support is built, QWidget and its subclasses are not reentrant. One consequence of this is that we cannot directly call functions on a widget from a secondary thread. If we want to, say, change the text of a QLabel from a secondary thread, we can emit a signal connected to QLabel::setText() or call QMetaObject::invokeMethod() from that thread. For example:

void MyThread::run()
    QMetaObject::invokeMethod(label, SLOT(setText(const QString &)),
                              Q_ARG(QString, "Hello"));

Many of Qt's non-GUI classes, including QImage, QString, and the container classes, use implicit sharing as an optimization technique. Although this optimization usually makes a class non-reentrant, in Qt this is not an issue because Qt uses atomic assembly language instructions to implement thread-safe reference counting, making Qt's implicitly shared classes reentrant.

Qt's QtSql module can also be used in multithreaded applications, but it has its own restrictions, which vary from database to database. For details, see http://doc.trolltech.com/4.3/sql-driver.html. For a complete list of multithreading caveats, see http://doc.trolltech.com/4.3/threads.html.

  • + Share This
  • 🔖 Save To Your Account