Thread Support in Qt
|
Iterator Type | Example classes | Support status |
---|---|---|
Input Iterator | Not Supported | |
Output Iterator | Not Supported | |
Forward Iterator | std::slist | Supported |
Bidirectional Iterator | QLinkedList, std::list | Supported |
Random Access Iterator | QList, QVector, std::vector | Supported and Recommended |
Random Access Iterators can be faster in cases where Qt Concurrent is iterating over a large number of lightweight items, since they allow skipping to any point in the container.
The non in-place modifying functions such as mapped() and filtered() makes a copy of the container when called. If you are using STL containers this copy operation might take some time, in this case we recommend specifying the begin and end iterators for the container instead.
Throughout the Qt documentation, the terms reentrant and thread-safe are used to specify how a function can be used in multithreaded applications:
By extension, a class is said to be reentrant if each and every one of its functions can be called simultaneously by multiple threads on different instances of the class. Similarly, the class is said to be thread-safe if the functions 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 C++ classes are inherently reentrant, since they typically only reference member data. Any thread can call such a member function on an instance of the class, as long as no other thread is calling a member function on the same instance. For example, the Counter class below is reentrant:
class Counter { public: Counter() { n = 0; } void increment() { ++n; } void decrement() { --n; } int value() const { return n; } private: int n; };
The class isn't thread-safe, because if multiple threads try to modify the data member n, the result is undefined. This is because C++'s ++ and -- operators aren't necessarily atomic. Indeed, they usually expand to three machine instructions:
If thread A and thread B load the variable's old value simultaneously, increment their register, and store it back, they end up overwriting each other, and the variable is incremented only once!
Clearly, the access must be serialized: Thread A must perform steps 1, 2, 3 without interruption (atomically) before thread B can perform the same steps; or vice versa. An easy way to make the class thread-safe is to protect all access to the data members with a QMutex:
class Counter { public: Counter() { n = 0; } void increment() { QMutexLocker locker(&mutex); ++n; } void decrement() { QMutexLocker locker(&mutex); --n; } int value() const { QMutexLocker locker(&mutex); return n; } private: mutable QMutex mutex; int n; };
The QMutexLocker class automatically locks the mutex in its constructor and unlocks it when the destructor is invoked, at the end of the function. Locking the mutex ensures that access from different threads will be serialized. The mutex data member is declared with the mutable qualifier because we need to lock and unlock the mutex in value(), which is a const function.
Most Qt 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 functions are thread-safe; these are mainly thread-related classes such as QMutex, or fundamental functions such as QCoreApplication::postEvent().
QThread inherits QObject. It emits signals to indicate that the thread started or finished executing, and provides a few slots as well.
More interesting is that QObjects can be used in multiple threads, emit signals that invoke slots in other threads, and post events to objects that "live" in other threads. This is possible because each thread is allowed to have its own event loop.
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 functions from another thread is not guaranteed to work. There are three constraints to be aware of:
Although QObject is reentrant, the GUI classes, notably QWidget and all its subclasses, are not reentrant. They can only be used from the main thread. 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 Mandelbrot and the Blocking Fortune Client example.
Each thread can have its own event loop. The initial thread starts its event loops using QCoreApplication::exec(); other threads can start an event loop using QThread::exec(). Like QCoreApplication, QThread provides an exit(int) function and a 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 Signals and Slots Across Threads section below.
A QObject instance is said to 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 for QObjects that are created before QApplication, QObject::thread() returns zero. This means that the main thread will only handle posted events for these objects; other event processing is not done at all for objects with no thread. Use the QObject::moveToThread() function to change the thread affinity for an object and its children (the object cannot be moved if it has a parent).
Calling delete on a QObject from another thread than the thread where it is created (or accessing the object in other ways) is unsafe unless you can guarantee that the object isn't processing events at the same moment. Use QObject::deleteLater() instead; it will post a DeferredDelete event, which the event loop of the object's thread will eventually pick up.
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 exec(), the QTimer will never emit its timeout() signal. Calling 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 function 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 postEvent()) can only be used to dispatch events to objects living in the thread from which the function is called.
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.
If you are calling a function on an QObject subclass that doesn't live in the current thread and the object might receive events, you must protect all access to your QObject subclass's internal data with a mutex; otherwise, you may experience crashes or other undesired behavior.
Like other objects, QThread objects live in the thread where the object was created -- 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.
Qt supports three types of signal-slot connections:
The connection type can be specified by passing an additional argument to connect(). Be aware that using direct connections when the sender and receiver live in different threads is unsafe if an event loop is running in the receiver's thread, for the same reason that calling any function on an object living in another thread is unsafe.
QObject::connect() itself is thread-safe.
The Mandelbrot example uses a queued connection to communicate between a worker thread and the main thread. To avoid freezing the main thread's event loop (and, as a consequence, the application's user interface), all the Mandelbrot fractal computation is done in a separate worker thread. The thread emits a signal when it is done rendering the fractal.
Similarly, the Blocking Fortune Client example uses a separate thread for communicating with a TCP server asynchronously.
Qt uses an optimization called 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 reentrant. The implicit sharing is really 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 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 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 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.
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
QPainter can be used to paint onto QImage, QPrinter, and QPicture paint devices. Painting onto QPixmaps and QWidgets is 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() function to detect whether or not font rendering can be used outside the GUI thread.
The QTextDocument, QTextCursor, and all related classes 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).
The QSvgGenerator and QSvgRenderer classes in the QtSvg module are reentrant.
Cette page est une traduction d'une page de la documentation de Qt, écrite par Nokia Corporation and/or its subsidiary(-ies). Les éventuels problèmes résultant d'une mauvaise traduction ne sont pas imputables à Nokia. | Qt qtextended4.4 | |
Copyright © 2012 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon, vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérêts. Cette page est déposée à la SACD. | ||
Vous avez déniché une erreur ? Un bug ? Une redirection cassée ? Ou tout autre problème, quel qu'il soit ? Ou bien vous désirez participer à ce projet de traduction ? N'hésitez pas à nous contacter ou par MP ! |
Copyright © 2000-2012 - www.developpez.com