Leistungskurs C++ Multithreading
Threading mit Qt Plattformübergreifende Thread-Klasse Sehr einfach zu benutzen Leider etwas schlecht dokumentiert Leistungskurs C++ 2
QThread Plattformübergreifende Thread-Klasse class MyThread : public QThread { Q_OBJECT protected: void run(); }; void MyThread::run() {... } Leistungskurs C++ 3
QThread Thread wird durch Instanziieren des QThread Objekts erzeugt. Nach Aufruf von QThread::start() wird der Code in der run() Methode eigenständig ausgeführt. Der Thread wird beendet, wenn der Code in run() ausgeführt wurde. Die aufrufende Funktion läuft sofort weiter. Wichtig: Nur der Code in run() läuft im neuen Thread, alle anderen Methoden, Slots, Member liegen im erzeugenden Thread (z.b. main) Leistungskurs C++ 4
Eintrittsinvarianz und Threadsicherheit Klassen sind eintrittsinvariant (reentrant), wenn mehrere Instanzen des Objekts gleichzeitig ausgeführt werden können, ohne sich gegenseitig zu beeinflussen. Mehrere Threads können also Methoden von unterschiedlichen Instanzen aufrufen. Regeln: Kein Zugriff auf static oder globale Variablen Der eigene Code darf nicht verändert werden Kein Aufruf von Methoden, die nicht eintrittsinvariant sind Leistungskurs C++ 5
Eintrittsinvarianz und Threadsicherheit Klassen sind threadsicher (thread-safe), wenn mehrere Threads die gleiche Instanz gleichzeitig benutzen können. Eine threadsichere Klasse ist immer auch eintrittsinvariant. Regeln: Zugriff auf Memberdaten muss serialisiert werden. Leistungskurs C++ 6
Eintrittsinvarianz und Threadsicherheit Beispiel für Eintrittsinvarianz: class Counter { public: Counter() { n = 0; } void increment() { ++n; } void decrement() { --n; } int value() const { return n; } private: int n; }; Leistungskurs C++ 7
Eintrittsinvarianz und Threadsicherheit Beispiel für nicht eintrittsinvarianten Code: int t; void swap(int *x, int *y) { t = *x; *x = *y; // Erneuter Aufruf von swap *y = t; } Leistungskurs C++ 8
Eintrittsinvarianz und Threadsicherheit Funktionen sind threadsicher (thread-safe), wenn sie von mehreren Threads gleichzeitig ausgeführt werden können, ohne sich gegenseitig zu beeinflussen. Zugriff auf gemeinsame Speicherbereiche muss geregelt werden. Möglichkeiten hierfür bieten Mutexe (Mutual Exclusive Lock, bietet Zugriffsregelung auf ein Objekt) und Semaphore (bietet Zugriffsregelung auf mehrere Objekte) Leistungskurs C++ 9
Eintrittsinvarianz und Threadsicherheit Mutex (mutual exclusion) QMutex mutex; int number = 6; void method1() { mutex.lock(); number *= 5; number /= 4; mutex.unlock(); } Leistungskurs C++ 10
Eintrittsinvarianz und Threadsicherheit Semaphor bietet Zugriffsschutz für mehrere gleichwertige Einheiten Anwendungsfall z.b. für Zugriff auf Ringpuffer (siehe QSemaphore Beispiel in Qt Dokumentation) QSemaphore sem(5); // sem.available() == 5 sem.acquire(3); // sem.available() == 2 sem.acquire(2); // sem.available() == 0 sem.release(5); // sem.available() == 5 Leistungskurs C++ 11
Eintrittsinvarianz und Threadsicherheit Beispiel für threadsicheren Code: 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; }; Leistungskurs C++ 12
Mögliche Fehler Race Condition void increment(int *n) { *n = *n + 1; } Undefiniertes Ergebnis bei Ausführung in mehreren Threads Leistungskurs C++ 13
Mögliche Fehler Deadlock wartet auf Ressource A belegt von Thread 1 Thread 2 belegt von Ressource B wartet auf Leistungskurs C++ 14
Mögliche Fehler Programme mit mehreren Threads sind kompliziert zu debuggen Wenn ein Thread abstürzt, stürzt das gesamte Programm ab Backtraces müssen auf jedem Thread einzeln untersucht werden Leistungskurs C++ 15
Multithreading mit QtConcurrent Automatische Erstellung von Threads und Aufteilung auf mehrere CPU-Kerne QtConcurrent::run() Führt einzelne Funktion in einem neuen Thread aus QtConcurrent::mapped() Führt eine Funktion auf allen Elementen einer Liste parallel aus Leistungskurs C++ 16
Threads in C++11 C++ Standard bietet seit 2011 eigene Thread-Klasse std::thread Erwartet Funktionszeiger als ersten Parameter des Konstruktors und beliebige weitere Parameter, die der Funktion übergeben werden. Zugriffsregelung über std::mutex std::lock_guard wird wie QMutexLocker verwendet Compiler muss neuen Standard unterstützen Compiler-Flags: -std=c++11 In qmake über QMAKE_CXXFLAGS setzen In cmake über CMAKE_CXXFLAGS setzen pthread Bibliothek muss (je nach Compiler) dazugelinkt werden Leistungskurs C++ 17
Links QThread http://qt-project.org/doc/qt-5.1/qtcore/thread-basics.html Leistungskurs C++ 21