Reentrancy und Thread-Safety
In der gesamten Dokumentation werden die Begriffe "reentrancy" und " thread-safe" verwendet, um Klassen und Funktionen zu kennzeichnen, um anzuzeigen, wie sie in Multithread-Anwendungen verwendet werden können:
- Eine thread-sichere Funktion kann gleichzeitig von mehreren Threads aus aufgerufen werden, auch wenn die Aufrufe gemeinsame Daten verwenden, da alle Verweise auf die gemeinsamen Daten serialisiert werden.
- Eine ablaufinvariante Funktion kann ebenfalls gleichzeitig von mehreren Threads aus aufgerufen werden, allerdings nur, wenn jeder Aufruf seine eigenen Daten verwendet.
Eine thread-sichere Funktion ist also immer reentrant, aber eine reentrante Funktion ist nicht immer thread-sicher.
Im weiteren Sinne gilt eine Klasse als reentrant, wenn ihre Mitgliedsfunktionen sicher von mehreren Threads aus aufgerufen werden können, solange jeder Thread eine andere Instanz der Klasse verwendet. Die Klasse ist thread-sicher, wenn ihre Mitgliedsfunktionen sicher von mehreren Threads aus aufgerufen werden können, selbst wenn alle Threads die gleiche Instanz der Klasse verwenden.
Hinweis: Qt-Klassen sind nur dann als thread-safe dokumentiert, wenn sie von mehreren Threads verwendet werden sollen. Wenn eine Funktion nicht als thread-safe oder reentrant gekennzeichnet ist, sollte sie nicht von verschiedenen Threads aus verwendet werden. Wenn eine Klasse nicht als thread-safe oder reentrant gekennzeichnet ist, sollte auf eine bestimmte Instanz dieser Klasse nicht von verschiedenen Threads aus zugegriffen werden.
Reentrancy
C++-Klassen sind oft ablaufinvariant, weil sie nur auf ihre eigenen Mitgliedsdaten zugreifen. Jeder Thread kann eine Mitgliedsfunktion auf einer Instanz einer ablaufinvarianten Klasse aufrufen, solange kein anderer Thread gleichzeitig eine Mitgliedsfunktion auf derselben Instanz der Klasse aufrufen kann. Die folgende Klasse Counter
ist zum Beispiel ablaufinvariant:
class Counter { public: Counter() { n = 0; } void increment() { ++n; } void decrement() { --n; } int value() const { return n; } private: int n; };
Die Klasse ist nicht thread-sicher, denn wenn mehrere Threads versuchen, das Datenelement n
zu ändern, ist das Ergebnis undefiniert. Der Grund dafür ist, dass die Operatoren ++
und --
nicht immer atomar sind. Sie bestehen in der Regel aus drei Maschinenbefehlen:
- Laden des Wertes der Variablen in ein Register.
- Inkrementieren oder Dekrementieren des Registerwerts.
- Speichern des Registerwerts zurück in den Hauptspeicher.
Wenn Thread A und Thread B gleichzeitig den alten Wert der Variablen laden, ihr Register inkrementieren und den Wert wieder speichern, überschreiben sie sich gegenseitig, und die Variable wird nur einmal inkrementiert!
Thread-Sicherheit
Es ist klar, dass der Zugriff serialisiert werden muss: Thread A muss die Schritte 1, 2, 3 ohne Unterbrechung (atomar) ausführen, bevor Thread B die gleichen Schritte ausführen kann; oder umgekehrt. Eine einfache Möglichkeit, die Klasse thread-sicher zu machen, besteht darin, alle Zugriffe auf die Datenelemente mit einem QMutex zu schützen:
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; };
Die Klasse QMutexLocker sperrt den Mutex automatisch in ihrem Konstruktor und hebt die Sperre auf, wenn der Destruktor am Ende der Funktion aufgerufen wird. Durch das Sperren des Mutex wird sichergestellt, dass der Zugriff von verschiedenen Threads aus serialisiert wird. Das Datenelement mutex
wird mit dem Qualifier mutable
deklariert, weil wir den Mutex in value()
sperren und entsperren müssen, was eine Konstantenfunktion ist.
Hinweise zu Qt-Klassen
Viele Qt-Klassen sind ablaufinvariant, werden aber nicht thread-sicher gemacht, da dies den zusätzlichen Overhead des wiederholten Sperrens und Entsperrens einer QMutex bedeuten würde. QString ist beispielsweise ablaufinvariant, aber nicht thread-sicher. Sie können sicher auf verschiedene Instanzen von QString von mehreren Threads gleichzeitig zugreifen, aber Sie können nicht sicher auf dieselbe Instanz von QString von mehreren Threads gleichzeitig zugreifen (es sei denn, Sie schützen die Zugriffe selbst mit einem QMutex).
Einige Qt-Klassen und -Funktionen sind thread-sicher. Dies sind hauptsächlich die Thread-bezogenen Klassen (z.B. QMutex) und grundlegende Funktionen (z.B. QCoreApplication::postEvent()).
Hinweis: Die Terminologie im Bereich Multithreading ist nicht vollständig standardisiert. POSIX verwendet Definitionen von "reentrant" und "thread-safe", die für seine C-APIs etwas unterschiedlich sind. Wenn Sie andere objektorientierte C++-Klassenbibliotheken mit Qt verwenden, stellen Sie sicher, dass die Definitionen verstanden werden.
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.