Prüfungshilfe
Threads
Was ist ein Thread?
Ein Thread ist ein Teil eines Programms, der unabhängig von anderen Programmteilen oder noch nicht vorhandenen Ergebnissen ausgeführt werden kann.
Speichermodell Prozess -> Thread -> Fork
Fork() kopiert alle Daten des Prozesses (wenn aus einem Thread aufgerufen, nur alle Stammdaten und den aufrufenden Thread sowie die eltern, restliche threads im prozess werden verworfen)
Register (PC, SP, ...)
Stack
Globale Variablen
Heap
Programm Text
zum Prozess gehörende Ressourcen
Potentieller Parallelismus
- voneinander unabhängige Aufgaben
d.h. Resultat ist unabhängig von der Reihenfolge der Ausführung!
Wann ist es sinnvoll Threads einzusetzen?
Wen die Aufgaben unabhängig voneinander un von der Zeit / Reihenfolge Ausgeführt werden können Gründe für pot. //
- blockender I/O
- sich überlappender I/O
- Asynchrone Ereignisse: Netz, Tastatur, Interrupts
- Realtime
Was sind die Voraussetzungen, die erfüllt sein müssen?
- Wenn unterschiedliche Ressourcen verwendet werden
- Wenn Unabhängigkeit von Resultaten anderer Tasks besteht
zu beachten: max. Konkurrenz und minimale Synchronisation je mehr Abhängigkeiten, um so mehr geblockte Tasks, welche aufeinander warten
In welchen typische Situationen ist ein Programm prallelisierbar?
- Starke CPU Beanspruchung
- kryptografische Funktionen, Matrizen, Kompression, etc.
- während ein oder mehrere Tasks Berechnungen durchführen, kann das Programm auf I/O reagieren
- evtl. Zuordnung einer CPU zu einer Berechnung
- asynchrone Ereignisse
- zufällige Intervalle zwischen Daten I/O
- Benutzer I/O, Netzwerk Aktivität, Hardware Interrupts, Sensoren
- Behandlungsroutinen können in einen Thread gekapselt werden unterschiedliche Behandlungsprioritäten
- einige Aufgaben sind wichtiger
- schnellere Reaktionszeit
- feste Bearbeitungszeit
- Ausführung an spezifischen Zeitpunkten
- Beispiele für MT Apps
- Server: Datenbanken, Fileserver, Printserver, HTTPd, P2P
- Rechnen & Signalprocessing
- Realtime für Server & Multitasking Apps
- effizienter als MP
- Scheduling
- weniger Komplexität bei asynch. Programmen
- Threads warten auf Ereignisse serielles Programm springt zwischen Kontexten durch Interrupts
Lebenslauf eines Threads
Anfang
Wen er mit pthread_create() erstellt wird
Ende
wenn er zur letzten anweisung im code des threads kommt oder zu einer return anweisung im Hauptcode des threads. Z.B. return(NULL)
Art (detached, joinable)
Detached: Ist losgelöst vom aifrufer Joinable: Aufrufer wartet auf beendigung
Ende des Prozesses
Wenn das ende des Main Blocks erreicht ist oder der Prozess ein kill signal vom OS bekommt.
Was ist unter Race, Deadlock und Starvation zu verstehen?
Race condition
Es wurden annahmen von Zeitlichen Abläufen im Programmcode angenommen, die zur runtime jedoch nicht bestanden. Es kann zu Abstürzen oder auch nur zu einem Fehlverhalten führen, wie zum beispiel falsche berechnungen usw.
Deadlock
Alle Threads warten auf ein Ereignis eines Threads, der befindet sich aber z.B. in einem Loop aus dem er nicht mehr herauskommt. Das Programm steht still.
Starvation
Z.B. Ein leser thread hat eine höhere priorität als der Schreiberthread auf eine mutex, der leser thread macht ein polling und verunmöglicht so den zugriff für den writer thread der eigentlich die infos abliefern möchte. Das programm steht still oder ist seeeeeeeehr langsam.
Modelle (Boss Slave, Peers, ...)
Wie sehen die Modelle aus?
Script ab Seite 38ff
Einsatzgebiete der jeweiligen Modelle
Voraussetzungen für den Einsatz, zu Beachten bei Implementation
nicht zu viel Kommunikation, nicht zu viel Teilen von Ressourcen etc.
Mix von Modellen
Buffering
Daten Austausch zwischen Threads
Weshalb?
Da man sonst Gefahr läuft, das ein writer warten muss weil er schneller ist als der reader oder umgekehrt.
Welche Punkte sind wichtig bei der Implementation (Lock, Zähler, ...)
Polling
Was ist das?
Wenn ein Thread in einer schlaufe (Timer) immer wieder nachfragt ob die Ressource frei ist oder geändert hat.
Weshalb ist das nicht gut?
Frisst CPU Time für andere Threads und macht eine grosse Last.
Wie kann man es verhindern?
Mit Condition Variables zum Beispiel.
Thread Pools
Weswegen?
Wie sieht Implementation aus?
Mutexe
- gemeinsame Daten schützen
- exklusiven Zugang zu Ressourcen bieten
- Kritische Sektionen (Critical Sections)
Wie funktionieren diese?
Script Seite 73ff
Wie werden sie angewendet?
- jede einzelne Ressource schützen
- Mutex definieren
- lock/unlock bei Zugriff
Warum ist _trylock nicht gut?
Wen man es benutzen muss, hat man bereits im Design Fehler gemacht. Es entspricht nicht dem Konzept von Mutexen ein Polling zu machen.
Script Seite 78ff
Bedingungs-Variablen
- Kontrolle des Zustands der Ressourcen
- Benachrichtigungssystem
Script Seite 85ff
Einsatz
- warten auf Bedingung (Signal) evtl. mit Timeout
- Signal:
pthread_cond_signal( &cv ) pthread_cond_broadcast( &cv )
Wie funktionieren diese
(Wake Up? Wer wacht auf? Wer bekommt Mutex?)
- Priorität
- FIFO
-> mit mehreren Prioritäten Starvation möglich!
- Broadcast - alle wachen auf und versuchen zu locken
pthread_once
Dieser code wird nur beim ersten aufrufenden thread ausgeführt für das ganze programm (z.B. initialize routines)
- egal wieviel Mal aufgerufen -> eine einzige Ausführung wird garantiert
- kein Thread beendet pthread_once bevor der erste pthread_once Thread beendet hat
Einsatz
wann, wie, warum?
Keys
- Thread spezifische Daten (vergleich mit Hash Tables)
- eine Art Pointer
- wenn dieser Pointer von einem Thread verwendet wird, zeigt er jedes Mal auf die Threadeigenen Daten
Script Seite 100 / 115ff
Einsatz
wann, wie, warum?
Cancellation
- Thread sollte nur an sicheren Punkten "cancellable" sein
- Zu jedem Thread gehört ein "Cleanup Stack"
Script Seite 123ff
Typen
enabled, deferred und automatic cancellation
Bedeutung der verschiedenen Typen
Enabled: Thread cancellation ist allgemein eingeschaltet für diesen thread deferred cancelation: Thread muss zuerst einen Cancel Point erreichen (verzögerte cancellation) Automatic: Werden von pthreads gesetzt
Funktionsweise
Cleanup stacks werden aufgerufen
Cancellation Points
Automatic:
- pthread_cond_wait
- pthread_cond_timedwait
- pthread_join
- sigwait
Source code:
- pthread_testcancel
Cleanup Stack
- sind für "Aufräumarbeiten" da
- werden auch bei pthread_exit ausgeführt
pthread_cleanup_push( aufraeum_routine, (void *)argument_p ) pthread_cleanup_pop( exec ) 1: ausführen 0: nicht ausführen
- push muss immer ein entsprechendes pop haben: kann als Macro definiert sein
Einsatz
weshalb, wie?
Mutex Attribute
Inheritance
Ceiling
Script Seite 147ff
Priority Inversion
Signale
Script Seite 156ff
Verwendung
empfangen/Masken
darauf reagieren
fork/exec
Script Seite 175ff
wie funktioniert dies im Pthread Kontext?
fork-handlng Stacks
weshalb?
wie werden diese eingesetzt?
C
typedef
Die Programmiersprache C bietet dem Benutzer die Möglichkeit, mit dem Schlüsselwort typedef eigene Datentypen zu definieren. Die Schaffung solcher eigenen Datentypen dient dabei im Wesentlichen dazu, die Lesbarkeit der Programme zu steigern. Durch das Schlüsselwort typedef werden zunächst Synonyme erzeugt, also gleichbedeutende Datentypen. Diese Synonyme können dann genauso verwendet werden, wie die von C vorgegebenen. Am häufigsten werden selbstdefinierte Datentypen dazu verwendet, zusammengesetzte Datenstrukturen wie Arrays oder Strukturen zu bezeichnen.
Beispiel
typedef int Ganze_Zahl; typedef char Zeichen; Ganze_Zahl a = 12; Zeichen b; b = 'e';
struct
Eine Struktur ist eine Folge von einem oder mehreren Elementen, die inhaltlich zusammengehören und über den Strukturnamen und den Elementnamen ansprechbar sind. In der Deklaration einer Struktur muss das Schlüsselwort struct erscheinen. Die einzelnen Elemente einer Struktur müssen nicht vom selben Typ sein, es sind auch komplexere Elemente wie Strukturen oder Arrays als Elemente einer Struktur zugelassen. Um die einzelnen Elemente anzusprechen gibt es zwei Wege. Zum einen kann ein Element über den Punkt (Bezeichner) angesprochen werden. Dazu muss der Name der Struktur und des Elementes, beide durch einen Punkt getrennt, eingegeben werden. Die andere Möglichkeit besteht darin die einzelnen Elemente über den Pfeil (Zeiger) anzusprechen. Für die Deklaration einer Struktur gibt es verschiedene Möglichkeiten. Hierbei muss zwischen Strukturmuster, das den Aufbau der Struktur angibt und keinen Speicherplatz benötigt, und der eigentlichen Struktur unterschieden werden.
Beispiel 1
/* Ohne Strukturmuster */
struct
{
char name[20];
long verdienst;
} akte; /* Definition einer Struktur akte */
Der Struktur wird der Name akte, über den sie dann auch angesprochen wird, zugeordnet.
Beispiel 2
/* Strukturmuster und Struktur */
struct AKTE
{
char name[20];
long verdienst;
};
struct AKTE akte; /* Definition der Struktur */
Zunächst wird ein Strukturmuster mit dem Namen AKTE definiert. Durch das Strukturmuster wird kein Speicherplatz belegt; die eigentliche Zuordnung des Strukturnamens erfolgt erst in der letzten Zeile.
Pointer
Ein Pointer (Zeiger) ist ein Datentyp, bei dem Speicheradressen verwaltet werden. Eine Variable von einem Pointertyp kann also jeweils eine konkrete Speicheradresse (z.B. von einer anderen statischen Variablen) beinhalten. Pointer sind insoweit dynamisch, als sie mit ihrer Deklaration zunächst keinen weiteren Speicherplatz zugewiesen bekommen als den für die eigentliche Adresse.
referenzieren
Datentyp *zeigervariable;
Der Datentyp des Zeigers muss vom selben Datentyp wie der sein, auf den er zeigt (referenziert).
dereferenzieren
Zeigervariable = &variable
zurückgeben aus Funktionen
Zeiger_Rückgabetyp *Funktionsname(Parameter)
Doppelte Pointer: wann werden diese verwendet?
- Zum iterieren
- Printer-friendly version
- Download PDF
- 536 reads

Post new comment