Using Qts Classes in Non-GUI Threads

Using Qt s Classes in Non GUI 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 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 are QThread, QMutex, QMutexLocker, QSemaphore, QThreadStorage, and QWaitCondition. In addition, the following functions are thread-safe: QApplication::postEvent(), QApplication::removePostedEvents(), QApplication::removePostedEvent(), and QEventLoop::wakeUp().

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 none of Qt's QObject subclasses are reentrant. One consequence of this is that we cannot directly call functions on a widget from a non-GUI thread. If we want to, say, change the text of a QLabel from a non-GUI thread, we must post a custom event to the GUI thread, asking it to change the text for us.

Deleting a QObject with delete is not reentrant. To delete a QObject from a non-GUI thread, we can call QObject::deleteLater(), which posts a "deferred delete" event.

QObject's signals and slots mechanism can be used in any thread. When a signal is emitted in one thread, the slots that are connected to it are called immediately, and the execution takes place in the same threadnot in the thread where the receiver object was created. This means that we can't use signals and slots to communicate with the GUI thread from other threads.

The QTimer class and the networking classes QFtp, QHttp, QSocket, and QSocketNotifier all depend on the event loop, so we cannot use them in non-GUI threads. The only networking class available is QSocketDevice, the low-level wrapper for the platform-specific networking APIs. A common technique is to use a synchronous QSocketDevice in a non-GUI thread. Some programmers find that it leads to simpler code than using QSocket (which works asynchronously), and by working in a non-GUI thread, they don't block the event loop.

Qt's SQL and OpenGL modules can also be used in multithreaded applications, but have their own restrictions, which vary from system to system. For details, see http://doc.trolltech.com/3.2/sql-driver.html as well as the Qt Quarterly article "Glimpsing the Third Dimension", available online at http://doc.trolltech.com/qq/qq06-glimpsing.html.

Many of Qt's non-GUI classes, including QImage, QString, and the container classes, use implicit or explicit sharing as an optimization technique. These classes are reentrant except for their copy constructors and assignment operators. When a copy of an instance of these classes is taken, only a pointer to the internal data is copied. This is dangerous if multiple threads attempt to modify the data simultaneously. In such cases, a solution is to use the QDeepCopy class when performing an assignment to an instance of an implicitly or explicitly shared class. For example:

QString password; QMutex mutex; void setPassword(const QString &str) { mutex.lock(); password = QDeepCopy(str); mutex.unlock(); }

Qt 4 will probably provide enhanced threading support. Among other things, it is expected that the signalslot mechanism will be extended to support connections across threads, eliminating the need to use custom events for communicating with the GUI thread. It is also expected that non-GUI classes like QSocket and QTimer will be available in non-GUI threads, and that QDeepCopy will no longer be necessary when copying instances of implicitly and explicitly shared classes across threads.

Категории