- C++11 Thread Variablenübergabe
Im besprochenen Beispielprogramm zu threads wird die Variable
accum als globale Variable definiert, was als schlechter
Programmierstil gilt. Besser ist es, diese Variable als Parameter zu
übergeben. Ändern Sie das Beispielprogramm, indem Sie einen Parameter
int& accum zu
square hinzufügen. Dies sollte eine
Referenz sein, da
accum sich ändern lassen sollte.
Allerdings kann der Aufruf nicht über
thread(&square, accum, i)
erfolgen, da dies eine Kopie von
accum erzeugt und
square
mit dieser Kopie aufruft. Verwenden Sie anstatt
ref(), d.h.
thread(&square, ref(accum), i).
Lösungsbeispiel: thread_lsg1.cpp
(nicht thread safe), thread_lsg1b.cpp
(thread safe)
- C++11 tasks
Verwenden Sie anstatt threads den
async-Befehl im beschriebenen
Programm zur parallelen Berechnung der Summe der Quadarate bis 20.
Fügen Sie hierzu die
future<int> Objekte zu einem
vector<future<int» hinzu. Am Ende des Programms iterieren Sie
über alle futures und erhalten den jeweiligen Rückgabewert und
fügen diesen zur Gesamtsumme hinzu.
Lösungsbeispiel: thread_lsg2.cpp
- C++11 tasks und this_thread
Implementieren Sie das C++11 task Beispielprogramm, wie in der vorhergehenden
Aufgabe beschrieben. Um festzustellen, ob das Programm tatsächlich parallel
ausgeführt wird, versuchen Sie folgendes:
- Fügen Sie eine Bildschirmausgabe der Variable
x in die
Methode
square hinzu und führen das neue Programm aus. Die
Bildschirmausgabe zeigt eine deterministische Ausgabe und keine besonderen
Anzeichen für paralleles Ausführen.
- Fügen Sie als nächstes eine
sleep-Aufruf zur
Methode
square hinzu, um eine CPU-intensive Berechnung zu simulieren:
this_thread::sleep_for(chrono::milliseconds(100));
Führen Sie das Programm wieder aus und bestimmen Sie die Ausführzeit.
Die tasks werden hintereinander ausgeführt und laufen nicht parallel.
- Fügen Sie eine Bildschirmausgabe von
this_thread::get_id()
zur Methode
square und zu
main hinzu. Auch die
Ausführung von main wird als thread aufgefasst - was ist die entsprechende
Bildschirmausgabe ?
- Die Funktion
async kann Methoden
asynchron oder
verzögert ausführen. Eine verzögerte Ausführung bedeutet
in unserem Beispiel, daß die Methode
square erst aufgerufen
wird, wenn
get() aufgerufen wird. Zusätzlich wird die Methode
im gleichen thread (also in diesem Fall
main()) ausgeführt.
Das Programm bzw. der Compiler sollte aber eine intelligente auf Geschwindigkeit
optimierte Entscheidung über den Ausführmodus treffen. GCC
verwendet aber als Standardeinstellung den verzögerten Modus. Eine
parallele bzw. asynchrone Ausführung kann erzwungen werden mit:
async(launch::async, &square, ...)
Fügen Sie diesen code zum Beispielprogramm hinzu und führen Sie diesen
wieder aus und messen die Ausführgeschwindigkeit - welche
Geschwindigkeitverbesserung erhalten Sie ?
Lösungsbeispiel: thread_lsg3.cpp
- Erzeuger-Konsumenten Problem
Das Erzeuger-Konsumenten (producer-consumer) Problem ist in einfacher Form
ein thread, der Waren erzeugt, und ein weiterer thread, der diese Waren
konsumiert. Dabei soll der consumer thread beim Warten eine conditions
Variable verwenden.
goods.push(i) und
good.pop() sollen sich
gegenseitig ausschließen, so daß die Daten nicht beschädigt werden.
Die Variablen
c++ und
c- sollen dabei als Kontrolle dienen,
da am Ende sich alles zu
0 addieren sollten.
Führen Sie folgendes Beispiel-Programm aus:
tconsumer.cpp.
Welche Ergebnisse erhalten Sie bei mehrfachem Ausführen ?
Versuchen Sie das Programm zunächst mit lock-unlock Blöcken zu reparieren
und führen das Programm 1000-10000 mal aus, um das Ergebnis zu verifizieren.
Versuchen Sie dann, conditions Variablen zu verwenden.
Lösungsbeispiel: thread_lsg4.cpp
- Aufzug Simulation
Nehmen Sie ein mehrstöckiges Haus an, in dem mehrere
Personen arbeiten. Die Personen wechseln gelegentlich die
Etagen und benutzen dazu einen 1-Personen Fahrstuhl
(also noch kleiner als in Schelling 4 ...).
Jede Person soll in einem eigenen Thread simuliert werden,
die Arbeit zwischen der Aufzugbenutzung wird durch
sleep(some-random-time)
simuliert. Aufzugfahren ist eine separate Klasse/Methode,
die Fahrzeit wird wiederum mit
sleep() proportional zur Distanz
simuliert.
Mittels
Mutex lock()/unlock() kann sichergestellt werden,
dass nur eine Person (=Thread) den Aufzug verwendet.
Ausgabe der Aktionen auf
stdout.
Studieren Sie den Einfluss von Zahl der Stockwerke,
Zahl der Beschäftigten, u.a.,
auf den Durchsatz.
Einfaches Beispielprogramm:
AufzugSimCpp11.cpp
(mit C++11 threads) bzw. AufzugSim.cpp
(mit QThreads).
- (Projekt) Primzahl-Finder Multi-Threaded
Ein einfaches Programm zum Finden von Primzahlen
prime_simple.C
soll so abgeändert bzw. erweitert werden, so daß die Suche
verteilt und unabhängig auf
verschiedenen Zahlenbereichen durchgeführt werden kann.
- Anschließend soll die Primzahlsuche in den verschiedenen Zahlenbereichen auf verschiedene
Threads verteilt werden.
- Lösungsbeispiel:
- OpenMP Beispiel zur Berechnung von Pi
PiOMP.cpp
ist ein
kleines Programm das Pi über Zufallszahlen berechnet und OpenMP zur
Parallelisierung verwendet.
Versuchen Sie es nachzuvollziehen.
Wozu braucht man
# pragma omp critical?
Warum benutzt man
drand48_r und
srand48_r( ...) ?
Modifizieren Sie das Programm und verwenden statt OpenMP
C++11
tasks bzw
async.