Threading in Qt Jambi


Threading in Qt Jambi

Qt Jambi provides a thread-safe way of posting events. This makes it easy to develop portable multithreaded Qt Jambi applications and take advantage of multiprocessor machines. Multithreaded programming is also a useful paradigm for performing time-consuming operations without freezing the user interface of an application.

An application can use the Java Thread class to create new threads. The synchronized and volatile keywords are used in the standard way to control access to objects in threads. However, \l{QObject}s (classes that inherit from QObject) can only be used in \l{QThread}s, and they can only be accessed from that thread. More on this in the \l{Threads and QObjects} section.

Qt Jambi also provides classes for thread handling, as an alternative to using synchronize and volatile. We discuss these briefly in the \l{The Threading Classes} section.

This document is intended for an audience that has knowledge of, and experience with multithreaded applications and Java thread handling.

Topics

The Threading Classes

Qt includes the following thread classes:

  • QThread inherits java.lang.Thread and is used when \l{QObject}s are used in threads.
  • QRunnable provides a runnable target, and contains the run() method, similar to java.lang.Runnable.
  • QThreadPool manages a collection of threads.
  • QMutex provides a mutual exclusion lock, or mutex.
  • QReadWriteLock provides a lock that allows simultaneous read access.
  • QSemaphore provides an integer semaphore (a generalization of a mutex).
  • QWaitCondition provides a way for threads to go to sleep until woken up by another thread.

The thread classes of Qt Jambi provides an alternative way to start and manage threads than using the Java thread handling, with its synchronized and volatile keywords.

Note that Qt Jambis's threading classes are implemented with native threading APIs; e.g., Win32 and pthreads. Therefore, they can be used with threads of the same native API.

Creating a Thread

You can create a thread by initializing a java.lang.Thread or QThread, and then call the start() method. For example:

    class MyJavaRunnable implements Runnable
    {
        @Override
        public void run()
        {
            // Do whatever
        }
    }
..
        MyJavaRunnable javaRunnable = new MyJavaRunnable();
        QThread runner = new QThread(javaRunnable);
        runner.start();

Qt Jambi also provides the QThreadPool class, which allows starting threads by providing a QRunnable. To create a thread in this way, subclass QRunnable and reimplement its \l{QThread::run()}{run()} method. For example:

    class MyJambiRunnable extends QRunnable
    {
        @Override
        public void run()
        {
            // Do whatever
        }
    }
..
        MyJambiRunnable jambiRunnable = new MyJambiRunnable();
        QThreadPool.start(jambiRunnable);

Note that QCoreApplication::exec() must always be called from the main thread (the thread that executes main()), not from a another thread. In GUI applications, the main thread is also called the GUI thread because it's the only thread that is allowed to perform GUI-related operations.

Synchronizing Threads

The QMutex, QReadWriteLock, QSemaphore, and QWaitCondition classes provide means to synchronize threads. While the main idea with threads is that they should be as concurrent as possible, there are points where threads must stop and wait for other threads. For example, if two threads try to access the same global variable simultaneously, the results are usually undefined.

QMutex provides a mutually exclusive lock, or mutex. At most one thread can hold the mutex at any time. If a thread tries to acquire the mutex while the mutex is already locked, the thread will be put to sleep until the thread that currently holds the mutex unlocks it. Mutexes are often used to protect accesses to shared data (i.e., data that can be accessed from multiple threads simultaneously). In the \l{Reentrancy and Thread-Safety} section below, we will use it to make a class thread-safe.

QReadWriteLock is similar to QMutex, except that it distinguishes between "read" and "write" access to shared data and allows multiple readers to access the data simultaneously. Using QReadWriteLock instead of QMutex when it is possible can make multithreaded programs more concurrent.

QSemaphore is a generalization of QMutex that protects a certain number of identical resources. In contrast, a mutex protects exactly one resource. The \l{threads/semaphores}{Semaphores} example shows a typical application of semaphores: synchronizing access to a circular buffer between a producer and a consumer.

QWaitCondition allows a thread to wake up other threads when some condition has been met. One or many threads can block waiting for a QWaitCondition to set a condition with \l{QWaitCondition::wakeOne()}{wakeOne()} or \l{QWaitCondition::wakeAll()}{wakeAll()}. Use \l{QWaitCondition::wakeOne()}{wakeOne()} to wake one randomly selected event or \l{QWaitCondition::wakeAll()}{wakeAll()} to wake them all. The \l{threads/waitconditions}{Wait Conditions} example shows how to solve the producer-consumer problem using QWaitCondition instead of QSemaphore.

Note that Qt's synchronization classes rely on the use of properly aligned pointers. For instance, you cannot use packed classes with MSVC.

QtConcurrent

The QtConcurrent namespace provides high-level APIs that make it possible to write multi-threaded programs without using low-level threading primitives such as mutexes, read-write locks, wait conditions, or semaphores. Programs written with QtConcurrent automatically adjust the number of threads used according to the number of processor cores available. This means that applications written today will continue to scale when deployed on multi-core systems in the future.

QtConcurrent includes functional programming style APIs for parallel list processing, including a MapReduce and FilterReduce implementation for shared-memory (non-distributed) systems, and classes for managing asynchronous computations in GUI applications:

  • QtConcurrent::map() applies a method to every item in a container, modifying the items in-place.
  • QtConcurrent::mapped() is like map(), except that it returns a new container with the modifications.
  • QtConcurrent::mappedReduced() is like mapped(), except that the modified results are reduced or folded into a single result.
  • QtConcurrent::filter() removes all items from a container based on the result of a filter method.
  • QtConcurrent::filtered() is like filter(), except that it returns a new container with the filtered results.
  • QtConcurrent::filteredReduced() is like filtered(), except that the filtered results are reduced or folded into a single result.
  • QtConcurrent::run() runs a method in another thread.
  • QFuture represents the result of an asynchronous computation.
  • QFutureIterator allows iterating through results available via QFuture.
  • QFutureWatcher allows monitoring a QFuture using signals-and-slots.
  • QFutureSynchronizer is a convenience class that automatically synchronizes several QFutures.

Qt Concurrent supports several STL-compatible container and iterator types, but works best with Qt containers that have random-access iterators, such as QList or QVector. The map and filter methods accept both containers and begin/end iterators.

Reentrancy and Thread-Safety

Throughout the Qt documentation, the terms reentrant and thread-safe are used to specify how a method can be used in multithreaded applications:

  • A reentrant method can be called simultaneously by multiple threads provided that each invocation of the method references unique data.
  • A thread-safe method can be called simultaneously by multiple threads when each invocation references shared data. All access to the shared data is serialized.
    • By extension, a class is said to be reentrant if each and every one of its methods can be called simultaneously by multiple threads on different instances of the class. Similarly, the class is said to be thread-safe if the methods can be called by different threads on the same instance.

      Classes in the documentation will be documented as thread-safe only if they are intended to be used by multiple threads.

      Note that the terminology in this domain isn't entirely standardized. POSIX uses a somewhat different definition of reentrancy and thread-safety for its C APIs. When dealing with an object-oriented C++ class library such as Qt, the definitions must be adapted.

      Most Qt Jambi classes are reentrant and not thread-safe, to avoid the overhead of repeatedly locking and unlocking a QMutex. For example, QString is reentrant, meaning that you can use it in different threads, but you can't access the same QString object from different threads simultaneously (unless you protect it with a mutex yourself). A few classes and methods are thread-safe; these are mainly thread-related classes such as QMutex, or fundamental methods such as QCoreApplication::postEvent().

      Threads and QObjects

      As mentioned previously, when a QObject is to be created in a thread, you need to use the QThread class. For all other Qt Jambi classes, java.lang.Thread can be used. QThread is started the same way as a java.lang.Thread.

              MyJavaRunnable javaRunnable = new MyJavaRunnable();
              QThread runner = new QThread(javaRunnable);
              runner.start();
      

      QObject Reentrancy

      QObject is reentrant. Most of its non-GUI subclasses, such as QTimer, QTcpSocket, QUdpSocket, QHttp, QFtp, and QProcess, are also reentrant, making it possible to use these classes from multiple threads simultaneously. Note that these classes are designed to be created and used from within a single thread; creating an object in one thread and calling its methods from another thread is not guaranteed to work. There are three constraints to be aware of:

      • The child of a QObject must always be created in the thread where the parent was created. This implies, among other things, that you should never pass the QThread object ( this) as the parent of an object created in the thread (since the QThread object itself was created in another thread).
      • Event driven objects may only be used in a single thread. Specifically, this applies to the \l{timers.html}{timer mechanism} and the \l{QtNetwork}{network module}. For example, you cannot start a timer or connect a socket in a thread that is not the object's thread.

      Note also that the GUI classes, notably QWidget and all its subclasses, can can only be used from the main thread (i.e., the thread where the QApplication object were initialized). As noted earlier, QCoreApplication::exec() must also be called from that thread.

      In practice, the impossibility of using GUI classes in other threads than the main thread can easily be worked around by putting time-consuming operations in a separate worker thread and displaying the results on screen in the main thread when the worker thread is finished. This is the approach used for implementing the \l{threads/mandelbrot}{Mandelbrot} example.

      Using Signals to Communicate Between QObjects in Separate Threads

      The functionality for the Qt Jambi signal mechanism is implemented in QSignalEmitter, which does not inherit QObject. It is therefore possible to send signals between \l{QObject}s living in different threads. The way to do this is to implement a class consisting of the signals to be sendt, and then emit or connect to these signals from the threads.

      Per-Thread Event Loop

      Each thread can have its own event loop. The initial thread starts its event loops using QApplication.initialize(). Other threads can start an event loop by creating a QEventLoop instance:

          class EventLoopThread implements Runnable
          {
              @Override
              public void run()
              {
                  QEventLoop loop = new QEventLoop();
                  loop.exec();
      
                  // Do whatever
                  
                  loop.exit();
              }
          }
      
      

      Like the main event loop started with QApplication::initialize(), QEventLoop provides an \l{QEventLoop::}{exit()} method and a \l{QEventLoop::}{quit()} slot.

      An event loop in a thread makes it possible for the thread to use certain non-GUI Qt classes that require the presence of an event loop (such as QTimer, QTcpSocket, and QProcess). It also makes it possible to connect signals from any threads to slots of a specific thread. This is explained in more detail in the \l{Signals and Slots Across Threads} section below.

      \image threadsandobjects.png Threads, objects, and event loops

      A QObject instance is said to \e live in the thread in which it is created. Events to that object are dispatched by that thread's event loop. The thread in which a QObject lives is available using QObject::thread().

      Note that \l{QObject}s must be created after the QApplication is \l{QApplication::}{initialize()}; if not, they will not be part of the main event loop. Use the QObject::moveToThread() method to change the thread affinity for an object and its children (the object cannot be moved if it has a parent).

      If no event loop is running, events won't be delivered to the object. For example, if you create a QTimer object in a thread but never call \l{QThread::exec()}{exec()}, the QTimer will never emit its \l{QTimer::timeout()}{timeout()} signal. Calling \l{QObject::deleteLater()}{deleteLater()} won't work either. (These restrictions apply to the main thread as well.)

      You can manually post events to any object in any thread at any time using the thread-safe method QCoreApplication::postEvent(). The events will automatically be dispatched by the event loop of the thread where the object was created.

      Event filters are supported in all threads, with the restriction that the monitoring object must live in the same thread as the monitored object. Similarly, QCoreApplication::sendEvent() (unlike \l{QCoreApplication::postEvent()}{postEvent()}) can only be used to dispatch events to objects living in the thread from which the method is called.

      Accessing QObject Subclasses from Other Threads

      QObject and all of its subclasses are not thread-safe. This includes the entire event delivery system. It is important to keep in mind that the event loop may be delivering events to your QObject subclass while you are accessing the object from another thread.

      Like other objects, QThread objects live in the thread where the object was created -- \e not in the thread that is created when QThread::run() is called. It is generally unsafe to provide slots in your QThread subclass, unless you protect the member variables with a mutex.

      On the other hand, you can safely emit signals from your QThread::run() implementation, because signal emission is thread-safe.

      Threads and Implicit Sharing

      Qt uses an optimization called \l{implicit sharing} for many of its value class, notably QImage and QString. Beginning with Qt 4, implicit shared classes can safely be copied across threads, like any other value classes. They are fully \l{#reentrant}{reentrant}. The implicit sharing is really \e implicit.

      In many people's minds, implicit sharing and multithreading are incompatible concepts, because of the way the reference counting is typically done. Qt, however, uses atomic reference counting to ensure the integrity of the shared data, avoiding potential corruption of the reference counter.

      Note that atomic reference counting does not guarantee \l{#thread-safe}{thread-safety}. Proper locking should be used when sharing an instance of an implicitly shared class between threads. This is the same requirement placed on all \l{#reentrant}{reentrant} classes, shared or not. Atomic reference counting does, however, guarantee that a thread working on its own, local instance of an implicitly shared class is safe. We recommend using \l{Signals and Slots Across Threads}{signals and slots} to pass data between threads, as this can be done without the need for any explicit locking.

      To sum it up, implicitly shared classes in Qt 4 are really implicitly shared. Even in multithreaded applications, you can safely use them as if they were plain, non-shared, reentrant value-based classes.

      Threads and the SQL Module

      A connection can only be used from within the thread that created it. Moving connections between threads or creating queries from a different thread is not supported.

      In addition, the third party libraries used by the QSqlDrivers can impose further restrictions on using the SQL Module in a multithreaded program. Consult the manual of your database client for more information

      Painting in Threads

      QPainter can be used to paint onto QImage, QPrinter, and QPicture paint devices. Painting onto QPixmaps and QWidgets is \e not supported. On Mac OS X the automatic progress dialog will not be displayed if you are printing from outside the GUI thread.

      Any number of threads can paint at any given time, however only one thread at a time can paint on a given paint device. In other words, two threads can paint at the same time if each paints onto separate QImages, but the two threads cannot paint onto the same QImage at the same time.

      Note that on X11 systems without FontConfig support, Qt cannot render text outside of the GUI thread. You can use the QFontDatabase::supportsThreadedFontRendering() method to detect whether or not font rendering can be used outside the GUI thread.

      Threads and Rich Text Processing

      The QTextDocument, QTextCursor, and \link richtext.html all related classes\endlink are reentrant.

      Note that a QTextDocument instance created in the GUI thread may contain QPixmap image resources. Use QTextDocument::clone() to create a copy of the document, and pass the copy to another thread for further processing (such as printing).

      Threads and the SVG module

      The QSvgGenerator and QSvgRenderer classes in the QtSvg module are reentrant.

      This page was updated at 17.05.2016