Leistungskurs C++ Multithreading
Zeitplan 16.10. Vorlesung 23.10. Vorlesung, Gruppeneinteilung 30.10. Vorlesung, HA1 06.11. Vorlesung, HA2 13.11. Vorlesung entfällt wegen SVV 20.11. Präsentation Vorprojekt 27.11. Präsentation Vorprojekt 04.12., 11.12., 18.12., 08.01. Working Sessions 15.01. Abschlusspräsentation 22.01. Abschlusspräsentation 29.01. Abschlusswettbewerb Leistungskurs C++ 2
Threading mit Qt Plattformübergreifende Thread-Klasse Leistungskurs C++ 3
QThread Plattformübergreifende Thread-Klasse class MyThread : public QThread { Q_OBJECT protected: void run(); }; void MyThread::run() {... } Leistungskurs C++ 4
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. Leistungskurs C++ 5
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++ 6
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++ 7
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++ 8
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++ 9
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++ 10
Eintrittsinvarianz und Threadsicherheit Mutex (mutual exclusion) QMutex mutex; int number = 6; void method1() { mutex.lock(); number *= 5; number /= 4; mutex.unlock(); } Leistungskurs C++ 11
Eintrittsinvarianz und Threadsicherheit Semaphor bietet Zugriff auf mehrere Einheiten 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++ 12
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++ 13
Mögliche Fehler Race Condition void increment(int *n) { *n = *n + 1; } Undefiniertes Ergebnis bei Ausführung in mehreren Threads Leistungskurs C++ 14
Mögliche Fehler Deadlock wartet auf Ressource A belegt von Thread 1 Thread 2 belegt von Ressource B wartet auf Leistungskurs C++ 15
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++ 16
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++ 17
Multithreading mit OpenMP #include <omp.h> int main () { int nthreads, tid; #pragma omp parallel private(tid) { tid = omp_get_thread_num(); printf("hello World from thread = %d\n", tid); if (tid == 0) { nthreads = omp_get_num_threads(); printf("number of threads = %d\n", nthreads); } } } Leistungskurs C++ 18
Multithreading mit OpenMP #pragma omp parallel private(variablenname) private (list) firstprivate (list) lastprivate (list) shared (list) Variable ist nicht initialisiert Variable wird mit Anfangswert initialisiert Variable wird am Ende kopiert Alle Threads können auf die Variable zugreifen (Absicherung nötig) Leistungskurs C++ 19
Multithreading mit OpenMP #include <omp.h> void omp_set_num_threads(int num_threads) /* Anzahl der Threads setzen auch über Umgebungsvariable möglich */ OMP_NUM_THREADS=2 Leistungskurs C++ 20
Links OpenMP https://computing.llnl.gov/tutorials/openmp/ QThread http://qt-project.org/doc/qt-4.8/threads.html Leistungskurs C++ 21