You are on page 1of 24

Parallele Programmierung:

Eine Einführung und die Verwendung von Threads

Facharbeit

im Fach
Informatik
Klasse 12.1

Werner-von-Siemens-Gymnasium Magdeburg

Marcel Bassüner

Fachlehrer: Herr Thormeyer

Wolmirsleben, 3. Januar 2007


Inhaltsverzeichnis
1 Einleitung 2

2 Parallelität: Varianten und Ebenen 4


2.1 Varianten der Parallelität . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2 Ebenen der Parallelität . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

3 Betriebssysteme für Parallelrechner 8


3.1 Monolithische Betriebssystemkerne . . . . . . . . . . . . . . . . . . . . 8
3.2 Minimalkerne (Low-Level-Kernel) mit ausgelagerten Betriebssystemfunk-
tionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.3 Objektorientierte, offene Betriebssysteme . . . . . . . . . . . . . . . . . 9

4 Parallele Programmiersprachen 11

5 Entwicklung paralleler Programme 13


5.1 Verwendung von Threads . . . . . . . . . . . . . . . . . . . . . . . . . . 13
5.2 Beispiel für Thread-Verwendung . . . . . . . . . . . . . . . . . . . . . . 14

6 Fazit 18

7 Anhang 19

8 Literaturverzeichnis 22

1
1 Einleitung

Wettervorhersage, Strömungsmechanik, Fahrzeugdynamik, usw. - Die Anforderungen


an Simulationen in der Informatik werden immer größer. Somit wächst auch ständig
der Drang nach leistungsstärkeren Systemen. Die zu bearbeitenden Programmpakete
werden immer riesiger, bei gleich bleibender Rechenleistung würden die Rechenzeiten
explodieren. 1 Die nur bedingt mögliche Optimierung der verwendeten Algorithmen
führte nicht zum gewünschten Erfolg, also mussten die Rechnersysteme selbst ver-
bessert werden. Jedoch ist neue Hardware teuer, und so wurden vorhandene Systeme
parallelisiert um die Leistung zu steigern. Natürlich war das nicht der einzige Grund
für die Entwicklung von parallelen Rechensystemen.
Der Mensch versucht seine Umwelt zu verstehen und vielfach bietet sich eine Copu-
tersimulation als Weg dorthin an. In der Natur laufen alle Vorgänge parallel ab,wie
zum Beispiel auf das Wachstum einer Pflanze eine Vielzahl von Faktoren gleichzeitig
wirken. Um diese komplexen Gegebenheiten nachzuvollziehen, zu simulieren, bedarf es
also auch paralleler Computersysteme. 2
Die Parallelität von Rechnerarchitekturen findet keinesfalls nur in wissenschaftlichen
Gebieten Anwendung, jeder von uns wird zu Hause an seine PC täglich damit kon-
frontiert. Wir finden paralleles Arbeiten im Betriebssystem selbst (z.B. Eingabe der
Tastatur - Ausgabe auf dem Monitor) jedoch auch in den einzelnen Benutzerpro-
grammen: in Spielen (Berechnung der KI) , im Internetbrowser (gleichzeitiges Laden
und Anzeigen der Internetseiten). 3

Parallele Rechnerarchitekturen sind ein aktuelles Thema in Forschung und Entwick-


lung. Derzeit halten besonders auch die Multicorerechner Einzug in die Wohnzimmer.
Wo noch vor einem Jahr der sequentielle Einkern PC stand stehen heute Computer
mit zwei oder sogar vier Prozessorkernen (Intel Core2Dou,AMD X2, QuadFX, usw.)

Das Thema dieser Facharbeit lautet Parallele Programmierung. Der Umfang der Pro-
blematik war Anlass dafür, die spezialisierte Ausarbeitung auf ”Heimcomputer”(PC)
und deren Software, zu beschränken

Als Informationsquelle für diese Facharbeit diente die Literaturrecherche verschieden-


artiger Quellen. Bei der Suche nach geeigneter Sachliteratur erwiesen sich Internettexte
als zu oberflächlich. Jedoch waren die Fachbücher zur Thematik, mit meist mehr als
10 Jahren, veraltet. Somit bildet diese Ausarbeitung eine Kombination der Aktualität
des Internets und dem fundierten Wissen der Bücher.

1
Mey, an Dieter: ”Parallel Programming in MPI. Parallelization Strategies”. Center for Computing
and Communication Aachen University of Technology, 28.01.2003
2
Bräunl, Thomas: ”Parallel Programmierung. Eine Einführung”. Paderborn: Vieweg, 1993 S.2
3
Spieß, Gunnar: ”Proseminar: Paralleles und verteiltes Rechnen. Thema: Parallele Programmierung
in Java”. Wilhelm-Schickard-Institut für Informatik - Abteilung Technische Informatik, 28.07.2003

2
Der Leser soll einen kleinen Einblick in parallele Architekturen bekommen und an
die parallele Programmierung heran geführt werden. Dazu werden folgende Fragen
gestellt:

• Wie wird Parallelität auf größtenteils sequentiellen Architekturen umgesetz?

• Wie kann man selbst Parallelität in Anwendungen einbauen?

3
2 Parallelität: Varianten und Ebenen
2.1 Varianten der Parallelität

Anwendungen durchlaufen auf Rechensystemen vielfältige Transformationen, welche


durch abstrakte oder reale Maschinen interpretiert werden. Jede dieser Maschinen ist,
durch die von ihr ausführbaren Instruktionen und durch die Objekte die durch die
Instruktionen verarbeitet werden, charakterisiert. Diese Charaktereigenschaften bilden
sogleich die Grundlage für die Entscheidung ob eine Maschine parallel ist oder nicht.
Unterschieden wird:

• Sequentielle Arbeitsweise

• Parallele Arbeitsweise

Bei verarbeitenden Maschinen, mit sequentieller Arbeitsweise werden die Instruktio-


nen streng nacheinander abgearbeitet. Wobei hingegen bei einer parallelen Arbeitsweise
mehrere Instruktionen zur gleichen Zeit ausgeführt werden. Bei der parallelen Verar-
beitung differenziert man weiter zwischen:

• Nebenläufigkeit

• Pipelining

Nebenläufigkeit liegt vor wenn mehrere Instruktionen zu einem Zeitpunkt auf asyn-
chron oder synchron arbeiten Einheiten ausgeführt werden.
Von Pipelining ist die Rede 4 : ”. . . wenn die Ausführung der Instruktionen der abstrak-
ten Maschine in synchron getaktete, sequentiell zu durchlaufende Teilausführungsschritte
zerlegt ist (die Phasen der Pipeline), die gegeneinander überlappt bearbeitet werden.” 5
Abbildung 1 stellt dies noch einmal grafisch dar.
Nebenläufigkeit und Pipelining ergänzen sich gegenseitig als Prinzipien und können
auch gemeinsam in abstrakten Maschinen vorkommen. 6

4
Waldschmidt, Klaus: ”Parallelrechner. Architekturen - Systeme - Werkzeuge.”.Leitfäden der Infor-
matik. Stuttgart, B.G. Teubner Stuttgart, 1995, S.13f
5
Waldschmidt: a.a.O. S.14
6
Ebd.

4
Abbildung 1: Pipelining
URL: http://de.wikipedia.org/wiki/Bild:Befehlspipeline.PNG“ (Stand 15.02.07)

5
2.2 Ebenen der Parallelität

Die Parallelität von Ereignissen kann auf verschieden Ebenen umgesetzt werden. So
unterscheidet man zwischen vier Stufen, wobei die eigentliche Parallelität, von Stufe zu
Stufe, immer feinkörniger wird. Jede Ebene vereint grundverschiedene Aspekte einer
parallelen Arbeitsweise. Konstrukte und Verfahrensweisen einer Ebene beschränken
sich auf diese und lassen sich meist nicht auf eine andere Ebene übertragen. Bei den
vier Ebenen handelt es sich um:

• Programmebene

• Prozedurebene

• Ausdrucksebene

• Bitebene

Programmebene

Dies ist die oberste Ebene, auf ihr laufen komplette Programme parallel (zumindest
scheinbar)ab. Der ausführende Computer muss in diesem Fall kein Parallelrechner sein,
es genügt ein Multitasking-Betriebssystem (z.B. durch das Zeitscheiben-Prinzip7 ).Die
Verarbeitungseinheiten der Ebene heißen Jobs und Tasks (daher hat der Windows
Taskmanager seinen Namen).

Prozedurebene

Auf dieser Ebene wird ein Programm in einzelne Programmfäden zerlegt, welche dann
nebenläufig arbeiten. Diese Programmteile nennt man Prozesse“ oder Theads8“ und
” ”
sind einzeln betrachtet sequentielle Prozeduren. Um dies Umzusetzen müssen Probleme
in unabhängige Teilprobleme zergliedert werden, damit es nur selten zu einem aufwen-
digen Datentransfer zwischen den Programmfäden, kommt. Anwendungsgebiete sind
nicht nur die allgemeine Parallelverarbeitung von Programmen sondern auch die Re-
gelung von Prozessrechnern zur Gleichzeitigen Ansprache von Hardwarekomponenten
bei der Robotersteuerung.

7
siehe Abschnitt 5
8
siehe Abschnitt 5

6
Ausdrucksebene

Hier werden arithmetische Ausdrücke durch ein einfaches Synchronverfahren parallel


ausgeführt. Auf dieser Ebene gibt es verschiedene Verfahrensweisen wie zum Beispiel
die Vektorisierung und die Datenparallelität. Die Datenparallelität bezieht sich auf die
Feingliedrigkeit der Ebene, dabei wird versucht fast jedem Datenelement einen eigenen
Prozessor zu zuordnen.

Bitebene

Die Letzte und somit fein körnigste Ebene ist die Bitebene. Die einzelnen Bit-Operation
werden in einem Wort vereint und dadurch parallelisiert.
Listing 1: Parallelität auf Bitebene
1 01100101
2 AND 10101101
3 00100101

9
Diese Schicht der Parallelität findet man in fast jedem Mikroprozessor.

9
Bräunl: a.a.O. S.11ff

7
3 Betriebssysteme für Parallelrechner
Die Eigenschaften eines Parallelrechners beeinflusst die Wahl eines geeigneten Betriebs-
systems sehr. Das Betriebssystem muss zum einen selbst die Parallelität nutzen und
zum anderen hat es die Aufgabe Anwendungen die Nutzung der Parallelität auf ein-
fachste weise möglich zu machen. Auch wenn in Einzelfällen Betriebssysteme speziell für
Parallelrechner entwickelt wurden, reich in der Regel die Zeitspanne zwischen erstem
Hardwareprototyp und benötigtem Betriebssystem nicht aus. Somit sind die Hersteller
Gezwungen auf bereits vorhandene System zurückzugreifen.
Im Folgenden werden drei Betriebssystemarten vorgestellt und auf ihre Tauglichkeit
für Parallelrechner untersucht, da parallele Architekturen meist für sehr rechenintensi-
ve Aufgaben benutzt werden. 10

3.1 Monolithische Betriebssystemkerne

Die Betreibssystemfunktionen sind in monolithischen Betriebssystemen alle zusam-


mengefasst. Zu diesen Basisfunktionen gehören Schnittstellenkommunikation, Hardwa-
retreiber und umfangreiche Dienstleistungsmodule, wie zum Beispiel:

• Prozessverwaltung

• Speicherverwaltung

• Dateisystem

• Netzwerkkommunikation

Diese bereitgestellten Mechanismen können meist nicht ausgewechselt werden, d.h sie
sind für alle Benutzer zwingend festgelegt. Größtenteils kann man auf nicht benötigte
Teile auch nicht verzichten dadurch verbrauchen diese unnötigerweise Speicher oder
sogar Rechenleistung. Ein weiterer Nachteil ist der große Zeitaufwand für Anwender-
programme um Systemfunktionen zu nutzen, da durch so genannte supervisor calls“

der richte Einstig in den Systemkern gefunden werden muss. Im Gegensatz dazu steht
die hervorragende Kommunikation innerhalb des Systemkerns.
In den 80er Jahren wurde versucht den Unix-Kern zu verkleinern. Dazu wurden Un-
tersuchungen durchgeführt welche Teile aus dem bisher monolithischen Kern entfernt
werden können. Aus dieser Arbeit resultierten die Minimalkerne mit ausgelagerten Be-
triebssystemfunktionen.

10
Waldschmidt: a.a.O. S.341ff

8
3.2 Minimalkerne (Low-Level-Kernel) mit
ausgelagerten Betriebssystemfunktionen

Durch die Auslagerung einiger Betriebssystemfunktionen entfällt die Möglichkeit direkt


auf alle Systeminformationen zuzugreifen. Dadurch sinkt zum einen die Geschwindig-
keit zwischen den verschiedenen Systemkomponenten, da diese nicht mehr direkt mit-
einander kommunizieren sondern die Adressraumgrenzen durch das System überwinden
müssen. Zum anderen wirkt sich dies jedoch positiv auf die Wartbarkeit des Systems
aus, weil nun die einzelnen Teile einfach ersetzt werden können. So kann zum Beispiel
ein neues Dateisystem eingegliedert werden ohne, dass der ganze Kern gewechselt wer-
den muss. Zusätzlich besteht die Möglichkeit es einfach zu einem bereits vorhandenen
hinzufügen. Somit sind die verschiedenen Anwendungen in der Lage selbständig zwi-
schen beiden Dateisystemen zu wählen. 11
Zusammenfassend haben Minimalkernsysteme folgende Eigenschaften:12

• Veränderungen und Erweiterungen an Systemfunktionen sind einfacher durch-


zuführen. Somit wird eine Justierung an die Bedürfnisse von Parallelrechnern
möglich.

• Die Modularisierung 13 ist aus Effizienzgründen besser, jedoch leider nur sehr
grob vorhanden. Dadurch können diese Module schnell zu Engpässen werden, da
innerhalb, keine Modulinterne Nebenläufigkeit realisierbar ist.

• Der Daten- und Informationsaustausch über Adressraumgrenzen hinaus ist zeit-


aufwendig und führt somit meist zu einem Effizienzverlust.

3.3 Objektorientierte, offene Betriebssysteme

Das Ziel war es den eingeschlagenen Weg weiter zu verfolgen, das heißt feinere Modula-
risierung. Jedoch muss das aufgetretene Kommunikationsproblem zwischen den Modu-
len behoben werden. Der Ansatz ist die Module die häufig miteinander kommunizieren
in den selben Adressraum zu schreiben. Dadurch verliert man die Modulkapselung,
welches eines der ersten Ziele war. Somit muss diese auf einem anderen Weg erreicht
werden.
Eine Möglichkeit ist die Überprüfung der Module bereits bei der Übersetzung ob die
Operationen modulübergreifend sind oder sich nur auf Daten innerhalb des Moduls
beziehen. Dies ist kein vollständiger Ersatz für die Kapselung jedoch reich es für An-
wendungen aus. Betriebssysteme sind wesentlich komplexer somit ist diese Variante
nicht hinreichend.
Einen Ausweg bietet die objektorientierte Programmierung, welche Hilfsmittel für die

11
Waldschmidt: a.a.O. S.356ff
12
Waldschmidt: a.a.O. S.357f
13
Module: Die einzelnen ausgegliederten Teile des Betriebssystems.

9
Bildung von Kommunikationsschnittstellen und die Kapselung bietet. Auf verschie-
dene Adressräume kann verzichtet werden da die einzelnen Objekte klar voneinander
abgegrenzt sind. Dadurch können wie im monolithischen Systemkernen wieder alle Be-
triebssystemobjekte in einen Adressraum gelegt werden, da nach wie von die Kapselung
bestehen bleibt.
Durch diesen Aufbau vermeidet man die Strukturierungsprobleme monolithischer Sys-
temkerne und kann trotzdem, durch die eindeutigen Schnittstellen der Objekte, An-
passungen an das System durchführen.
Durch die feine Strukturierung der Objekte kann auf objektinterne Nebenläufigkeit ver-
zichtet werden. Demzufolge müssen nur die Kommunikationsschnittstellen koordiniert
werden.14

14
Waldschmidt: a.a.O. S.358ff

10
4 Parallele Programmiersprachen
Programmiersprachen geben uns die Möglichkeit parallele Architekturen zu nutzen.
Durch sie können Programme geschaffen werden welchen die Parallelität nutzen.
Als erstes unterscheidet man vier Sprachparadigmen:

• Imperative Programmiersprachen (Fortran,C,. . . )

• Logische Programmiersprachen (Prolog,. . . )

• Objektorientierte Programmiersprachen (Delphi,. . . )

• Funktionale Programmiersprachen (Haskell,. . . )

Für alle diese Gruppen existieren Möglichkeiten um sie für parallele Programmierung
zu nutzen. Es gibt einige Ansätze bei denen Versucht wurde bestehende Sprachen
abzuwandeln und zu erweitern, um sie für parallele Anwendungen zu nutzen, wie zum
Beispiel für C++ und Fortran (OpenMP, Cilk). Jedoch setzten sich im Allgemeinen
Bibliotheken (PVM, MPI, Pthreads,Win32 Threads) als Erweiterungen durch. Sehr
seltene Fälle sind die extra entwickelten Sprachen (symmetrische Sprache, Erlang) für
parallele Programmierung.
Egal welche Sprache gewählt wird, es folgt eine weitere Unterscheidung. Es wird nach
zwei Ansätzen für die Verarbeitung der Parallelität differenziert:

Implizite Parallelität ist die einfachere Methode für den Programmierer, da keine
Änderungen an einem bestehenden sequentiellen Quellcode vorgenommen wer-
den müssen. Bei diese Form der Parallelverarbeitung gibt es zwei Möglichkeiten:

1. Man kann einzelne sequentielle Funktionen durch den Aufruf von Methoden,
aus parallelen Bibliotheken ersetzen. Meistens sind die Bibliotheken sehr
begrenzt und nur für mathematische Algorithmen (Intel MKL) zu finden.

2. Moderne Compiler wandeln sequentiellen Code beim Erstellen automatisch


in parallele Instruktionsströme um. Die Rechenoperationen werden auf die
vorhandenen CPU-Kerne verteilt. Jedoch ist gibt es auch bei diesem Modell
noch einige Einschränkungen, wodurch nur einfache Schleifenoperationen
und bestimmte arithmetische Funktionen möglich sind. Ein Beispiel für diese
Compiler wäre der aktuelle Intel Compiler: Compiler Switch:/Qparallel.15

Explizite Parallelität setzt oftmals eine umfassende Änderung des sequentiellen Quell-
textes voraus. Auch hier gibt es verschiedene Varianten der Umsetzung. Bei einem
inkrementellen Ansatz handelt es sich um rechenintensive Bereiche die paralleli-
siert werden (OpenMP). Eine andere Methode ist die Nebenläufigkeit von rechen-

15
Dr. Deilmann, Mario: Parallelwelten“,in entwickler magazin“. S&S: Ausgabe Januar/Februar 2007.
” ”
S.26ff

11
intensiven Bereichen, mit der Steuerung eines Kontrollflusses (Threads, MPI).
Wiederum vertreten Threads und MPI verschiedene Klassen. Bei den Threads
werden Anwendungen in einzelne, voneinander weitest gehend unabhängige Teil-
probleme zerlegt. Diese Programmfäden“ werden nebenläufig organisiert. Hier-

bei wird über einen gemeinsamen Speicher kommuniziert.
MPI hingegen regelt die Kommunikation der Teilprobleme über Nachrichten.
Somit erklärt sich auch deren Verwendung in Netzwerken. Von einem Steuer-
rechner wird das Programm gestartet. Die einzelnen Teilprobleme laufen nun
jeweils auf einem Rechner im Netzwerk wodurch die Parallelität erreicht wird.
Die Ergebnisse der Berechnungen werden anschließend per Nachrichten über das
Netzwerk versendet.16 Häufig stellt dadurch die Kommunikation zwischen den
Programmfäden“ einen Engpass dar, weshalb die Nachrichten Übertragung so

gering wie möglich gehalten werden sollte.

16
Ebd.

12
5 Entwicklung paralleler Programme
Der letzte Punkt dieser Arbeit befasst sich mit der eigenen Verwendung von Parallelität
in Anwendungen. Parallelität kann in verschieden Situationen sehr hilfreich und manch-
mal auch nötig sein. Wie schon in der Einleitung angesprochen laufen in Simulationen
viele Dinge parallel ab. Somit wäre sie ein Beispiel für die Performancesteigerung durch
Parallelität. Zum anderen gibt es auch die Möglichkeit zur Erhöhung der Sicherheit.
Dies kommt zum Beispiel bei der Spiegelung von Festplatten zum Einsatz, Änderungen
werden parallel an der original und an der gespiegelten Festplatte vorgenommen.

5.1 Verwendung von Threads

Bei dem Thread-System wird das Problem in Teilprobleme zerlegt, in so genannte


Programmfäden“. Diese Programmfäden werden von dem Scheduler gesteuert und

abgearbeitet. 17 Sobald mehr Teilprobleme als Prozessoren vorhanden sind geschieht
die Parallelisierung über time sharing“.Bei dem time sharing“ oder auch Zeitschei-
” ”
benverfahren wird die zur Verfügung stehende Rechenzeit geteilt. Jeder Thread darf
eine bestimmte Zeit arbeiten, dann wird er gestoppt und der Nächste ist dran. Dies pas-
siert so schnell, dass es im Endeffekt parallel wirkt. Threads die in dem Moment nicht
arbeiten in dem ihnen Zeit zur Verfügung steht werden Übersprungen, dadurch haben
andere Fäden mehr Rechenzeit. 18 Das Microsoft Windows Betriebssystem basiert auch
auf dem Zeitscheibenprinzip. Wenn eine Anwendung sehr viel Rechenzeit benötigt, wie
zum Beispiel bei einer Endlosschleife (ungewollt), haben andere Programme kaum eine
Chance selbst zu arbeiten. Aus diesem Grund hat man verschiedene Prioritäten ein-
geführt, womit bestimmte Anwendungen genügend Rechenzeit bekommen. Der Task-
manager ist eines der Programme mit der höchsten Priorität. Dadurch ist es möglich
diesen zu starten wenn sich fast alles andere aufgehängt“ hat.

Eine wietere Besonderheit die man bei Umgang mti Parallelität beachten muss nennt
sich Synchronisation. Bei mehreren Prozessen die unabhängig von einander sind stellt
dies keon problem dar. Sobald die Threads jedoch untereinander kommunizieren oder
die selben Hardwarekomponeten nutzen wird es schwierig. 19 Ein beliebtes Beispiel für
einen solchen Fall ist die Ausgabe über einen Drucker. Man stelle sich vor es arbeiten
zwei Threads parallel und der erste gibt nun die berechneten Daten über den Drücker
aus. Kurz darauf, der Druckvorgang ist noch nicht beendet, möchte der zweite Thread
seine Daten auch drucken. An dieser Stelle gibt es nun eine gleichzeitige Nutzung von
sogenannten Betriebsmitteln. Dies Teile des Quelltextes bekamen den Namen Kriti-

sche Abschnitte“. Um das entstandene Problem zu lösen gab es verschiedene Ansätze.

Die erste Variante ware Flags“. Das sind Status-Flaggen die den Zustand der Be-

triebsmittel anzeigen. Wenn der Drucker nun von einem Prozess verwendet wird die
17
Burkhard,Steffen: Parallele Rechnersysteme. Programmierung und Anwendung“. Berlin, München:

Verl. Technik, 1993. S.98
18
Bräunl:a.a.O. S.12
19
Spieß: a.a.O S.6

13
Drucker-Flagge auf besetzt gestellt. Kommen zu diesem Zeitpunkt weitere Druckeran-
fragen werden diese Prozesse in eine Warteschleife gestellt bis der Drucker wieder frei-
gegeben ist. Das Freigeben geschieht wenn die Drucker Nutzung eines Prozesses abge-
schlossen ist. In dem Augenblick in dem das passiert bekommen alle Prozesse die sich in
der Warteschlange befanden die Möglichkeit dem Drucker befehle zu erteilen. Dadurch
kam es in einigen Situationen wieder zu Überschneidungen.Es musste also eine andere
Lösung für das Problem gefunden werden.
Diese neue Lösung nannte sich Semaphore. Das Prinzip der Semaphoren wurde 1965
erstmals von E. W. Dijkstra beschrieben. Das Wort selbst kam aus dem Griechischen
und bedeutet Signal. Die Grundstruktur dieser Idee ist den Flags sehr ähnlich. Eine
Semaphore besteht aus zwei Variablen und zwei Prozeduren. Die erste Variable ist ein
Zähler der Werte größer gleich Null annehmen kann. Die Zweite ist eine Warteschlange
in die sich Prozesse nach dem FIFO (first in, first out) einordnen. Dadurch vermei-
det man einen doppelten Zugriff auf Betriebsmittel. Bei der Initialisierung nimmt die
Warteschlange den Wert Leer an und der Zähler den Wert Eins. Beim Eintritt ei-
nes Prozesses wird durch den Aufruf der Verarbeitungsprozedure WAIT“ der Zähler

dekrementiert. Solange dies geschieht wenn der Zähler größer Null ist kann der Pro-
zess die Ressource nutzen, sobald er gleich Null ist werden einkommende Prozesse in
die Warteschlange aufgenommen. Anders als bei den Flags durchlaufen sie nun keine
Schleife sondern pausieren. Somit wird deren Rechenzeit nicht verschwendet und an-
dere Prozesse könne diese gewonnene Rechenkapazität nutzen. Wenn ein Prozess das
Betriebsmittel freigibt wird die Prozedur Signal“ aufgerufen, welche den Nächsten aus

der Warteschlange holt und auf die Ressource zugreifen lässt.

5.2 Beispiel für Thread-Verwendung

Die Verwendung von Parallelität wird an der Simulation von einem Einkaufsmarkt ver-
deutlicht: In den Markt kommen regelmäßig neue Kunden, die sich eine zufällige Zeit
im Laden aufhalten und dann zur Kasse gehen. Es gibt eine Warteschlange. Wenn eine
der parallel arbeitenden Kassen einen Kunden abgearbeitet hat, kann der nächste aus
der Warteschlange nachrücken. Die Kassen sollen nach belieben geöffnet oder geschlos-
sen werden können. Jedoch wenn keine Kasse geöffnet ist, ist der Markt geschlossen
und es kommen keine weiteren Kunden (Pause).

Um die Parallelverarbeitung zu realisieren wird das Thread-System verwendet. Als


Arbeitsplattform dient Windows und somit die Win32 API in der die nötigen Kompo-
nenten für die Verwendung von Threads gespeichert sind. Die gewählte Programmier-
sprache ist in diesem Fall Delphi20 .

Um Threads unter Delphi verwenden zu können, müssen extra Klassen von der Ur-
klasse TThread (uses classes) abgeleitet werden. Dies geschieht damit die Prozedur
Execute“ überschrieben wird, in der später enthalten ist was der Thread eigentlich

macht.

20
Borland Delphi7

14
Listing 2: Klassen Vererbung
1 type
2 Kasse = class ( TThread )
3 private
4 { Private - Deklarationen }
5 protected
6 procedure Execute ; override ;
7 end ;
8
9 type
10 Customer = class ( TThread )
11 private
12 { Private - Deklarationen }
13 protected
14 procedure Execute ; override ;
15 public
16 end ;

Zwei verschiedene Klassen werden benötigt, da im folgenden auch zwei verschiedene


Arten von Threads verwendet werden. Die Erste ist die Kasse wovon es mehrere geben
kann welche parallel arbeiten müssen. Zusätzlich gibt es einen Programmfaden“ der

Kunden in das Geschäft bring, somit muss auch diese parallel laufen. Diese eben be-
nannten Threads sind Nebenthreads. Der Hauptthread (Form1 Prozeduren) bleibt frei
für Ein- und Ausgabe.
Im Anschluss werden die Klassen und anderen Variablen verfügbar gemacht. Deren
Deklaration erfolgt unter private“, damit sie in dem ganzen Form1 bereitstehen je-

doch noch von anderen Objekten verändert werden könne, da das nur im Hauptthread
geschehen darf.
Listing 3: Deklaration
1 private
2 { Private - Deklarationen }
3 WaitCustomer : integer ;
4 LeaveCustomer : integer ;
5 OpenTill : byte ;
6 Customer : TCustomer ;
7 TKassen : array [1..10] of kasse ;

Passend zu den Variablen müssen somit Prozeduren erstellt werden die die Aufgabe
der Veränderung übernehmen.

15
Listing 4: Variablen ändern
1 procedure TForm1 . IncCustomer ;
2 begin
3 inc ( WaitCustomer ) ;
4 LWaitCustomer . Caption := IntToStr ( WaitCustomer ) ;
5 end ;
6

7 procedure TForm1 . DecCustomer ;


8 begin
9 if WaitCustomer >0 then
10 begin
11 dec ( WaitCustomer ) ;
12 inc ( LeaveCustomer ) ;
13 LCustomer . Caption := IntToStr ( LeaveCustomer ) ;
14 LWaitCustomer . Caption := IntToStr ( WaitCustomer ) ;
15 end ;
16 end ;

Damit die Threads auch etwas tun können, müssen die Befehle in die überschriebene
Execute Prozedur.
Listing 5: Execute
1 // - - - - - - - - - - - - - - - - - - - - -
2 // TCustomer
3 // - - - - - - - - - - - - - - - - - - - - -
4
5 procedure TCustomer . Execute ;
6 begin
7 while Terminated = false do
8 begin
9 sleep ( random (2000) ) ;
10 Synchronize ( Form1 . IncCustomer ) ;
11 end ;
12 end ;
13
14 // - - - - - - - - - - - - - - - - - - - - -
15 // KASSE
16 // - - - - - - - - - - - - - - - - - - - - -
17
18 procedure Kasse . Execute ;
19 begin
20 while Terminated = false do
21 begin
22 sleep (2000+ random (3000) ) ;
23 Synchronize ( Form1 . DecCustomer ) ;
24 end ;
25 end ;

Da die Threads in dem Beispiel nur die Aufgabe haben die Variablen zu ändern sind die
Prozeduren recht übersichtlich. Um jedoch Veränderungen vorzunehmen muss die Pro-
zedur (z.B. TForm1.IncCustomer) durch den Befehl: Synchronize(); aufgerufen werden.
Dieses Kommando übernimmt die Arbeit der Semaphore oder der Flags und synchro-
nisiert alle zugreifenden Threads automatisch.

16
Letztlich fehlt noch der Start/ Aufruf der Threads. Hier ist dies an den FormCreate
Prozedur demonstriert.
Listing 6: Start
1 procedure TForm1 . FormCreate ( Sender : TObject ) ;
2 begin
3 Customer := TCustomer . Create ( true ) ;
4 LeaveCustomer :=0;
5 end ;

Customer wird durch den Konstruktor initialisiert und gestartet. Die Boolean Variable
in der Klammer bestimmt ob der Thread pausierend gestartet wird. Der Wert true
startet ihn im Pausen-Modus. Ansonsten wird sofort mit der Ausführung der Execute
Prozedur begonnen.
So sieht das fertige Programm aus:

Abbildung 2: Einkaufsmarkt

17
6 Fazit
Paralle Programmierung ist ein sehr umfassendes Thema. Ausarbeitungen darüber
könnten hunderte von Seiten lang sein. Jedoch Setzte sich diese Ausarbeitung zum
Ziel einen Einblick zu gewähren und den Anwendern, also den Programmierern etwas
näher zu bringen.
Parallelität kann man auf sequentiellen Architekturen auf verschiedenste Weise umset-
zen. Zum Beispiel durch nachrichtenorientierte Systeme wie zum Beispiel MPI, welches
häufig in Netzwerken wie in Unis eingesetzt wird. Moderne Compiler wie Intels /Qpar-
allel bieten die Möglichkeit sequentiellen Quelltext einfach zu parallelisieren, jedoch
sind diese im Umfang noch sehr eingeschränkt. Eine weitere Methode ist der Einsatz
von Bibliotheken die es ermöglichen parallel zu arbeiten. Eine der bekanntesten ist die
Thread-Bibliothek mit dem time sharing“ Verfahren. Nach diesem Prinzip arbeiten

die meisten etablierte Betriebssysteme.
Die Verwendung von Threads in Delphi ist sehr einfach. Um kleinere Konstrukte zu
erstellen muss man nur eine neue Klasse von TThread erben lassen und die Execute
Prozedur überschreiben.
Listing 7: Vererben
1 type
2 Thread = class ( TThread )
3 private
4 { Private - Deklarationen }
5 protected
6 procedure Execute ; override ;
7 end ;

Die Prozedur Execute enthält den Code der beim ausführen den Threads abgearbeitet
wird. Nach dem folgenden Schema wird ein Thread erzeugt und gestartet.
Listing 8: Erzeugung
1 TestThread := Thread . Create ( false ) ; // true -> Pause

Mit dem Befehl Suspend kann man einen Thread anhalten und mit de Befehl Resume
wieder fortsetzen.
Listing 9: Steuerung
1 TestThread . Suspend ;
2 TestThread . Resume ;

Mit dem Kommando Terminate besteht die Möglichkeit einen Thread zu beenden.
Wenn in dem Thread eine Endlosschleife ablaufen soll, muss als Abbruchbedingung
Terminated stehen.
Listing 10: Beenden
1 TestThread . Terminate ;
2 TestThread . Terminated = True / False

18
7 Anhang
Listing 11: Einkaufsmarkt
1 unit Markt ;
2
3 interface
4
5 uses
6 Windows , Messages , SysUtils , Variants , Classes , Graphics , Controls ,
Forms ,
7 Dialogs , StdCtrls , ExtCtrls ;
8
9
10 type
11 Kasse = class ( TThread )
12 private
13 { Private - Deklarationen }
14 protected
15 procedure Execute ; override ;
16 end ;
17

18 type
19 TCustomer = class ( TThread )
20 private
21 { Private - Deklarationen }
22 protected
23 procedure Execute ; override ;
24 public
25 end ;
26
27
28 type
29 TForm1 = class ( TForm )
30 KasseHinzu : TButton ;
31 Label2 : TLabel ;
32 LWaitCustomer : TLabel ;
33 Label1 : TLabel ;
34 LOpenTill : TLabel ;
35 Bevel1 : TBevel ;
36 KasseWeg : TButton ;
37 Label3 : TLabel ;
38 LCustomer : TLabel ;
39 Label4 : TLabel ;
40 procedure IncCustomer ;
41 procedure DecCustomer ;
42 procedure IncTill ;
43 procedure DecTill ;
44 procedure FormCreate ( Sender : TObject ) ;
45 procedure KasseHinzuClick ( Sender : TObject ) ;
46 procedure KasseWegClick ( Sender : TObject ) ;
47 procedure FormClose ( Sender : TObject ; var Action : TCloseAction ) ;
48 private
49 { Private - Deklarationen }
50 WaitCustomer : integer ;
51 LeaveCustomer : integer ;

19
52 OpenTill : byte ;
53 Customer : TCustomer ;
54 TKassen : array [1..10] of kasse ;
55 public
56 { Public - Deklarationen }
57 end ;
58
59 var
60 Form1 : TForm1 ;
61
62 implementation
63
64 { $R *. dfm }
65
66 // - - - - - - - - - - - - - - - - - - - - -
67 // TCustomer
68 // - - - - - - - - - - - - - - - - - - - - -
69
70 procedure TCustomer . Execute ;
71 begin
72 while Terminated = false do
73 begin
74 sleep ( random (2000) ) ;
75 Synchronize ( Form1 . IncCustomer ) ;
76 end ;
77 end ;
78

79 // - - - - - - - - - - - - - - - - - - - - -
80 // KASSE
81 // - - - - - - - - - - - - - - - - - - - - -
82
83 procedure Kasse . Execute ;
84 begin
85 while Terminated = false do
86 begin
87 sleep (2000+ random (3000) ) ;
88 Synchronize ( Form1 . DecCustomer ) ;
89 end ;
90 end ;
91

92 // - - - - - - - - - - - - - - - - - - - - -
93 // FORM1
94 // - - - - - - - - - - - - - - - - - - - - -
95
96 procedure TForm1 . IncTill ;
97 begin
98 if OpenTill <10 then
99 begin
100 inc ( OpenTill ) ;
101 TKassen [ OpenTill ]:= Kasse . Create ( false ) ;
102 LOpenTill . Caption := IntToStr ( OpenTill ) ;
103 end ;
104 end ;
105
106 procedure TForm1 . DecTill ;
107 begin

20
108 TKassen [ OpenTill ]. Terminate ;
109 dec ( OpenTill ) ;
110 LOpenTill . Caption := IntToStr ( OpenTill ) ;
111 end ;
112

113 procedure TForm1 . IncCustomer ;


114 begin
115 inc ( WaitCustomer ) ;
116 LWaitCustomer . Caption := IntToStr ( WaitCustomer ) ;
117 end ;
118

119 procedure TForm1 . DecCustomer ;


120 begin
121 if WaitCustomer >0 then
122 begin
123 dec ( WaitCustomer ) ;
124 inc ( LeaveCustomer ) ;
125 LCustomer . Caption := IntToStr ( LeaveCustomer ) ;
126 LWaitCustomer . Caption := IntToStr ( WaitCustomer ) ;
127 end ;
128 end ;
129
130 procedure TForm1 . FormCreate ( Sender : TObject ) ;
131 begin
132 Customer := TCustomer . Create ( true ) ;
133 LeaveCustomer :=0;
134 end ;
135
136 procedure TForm1 . KasseHinzuClick ( Sender : TObject ) ;
137 begin
138 IncTill ;
139 if OpenTill =1 then
140 Customer . Resume ; // Thread fortsetzen
141 if OpenTill =10 then KasseHinzu . Enabled := false ;
142 if OpenTill >0 then KasseWeg . Enabled := true ;
143 end ;
144

145 procedure TForm1 . KasseWegClick ( Sender : TObject ) ;


146 begin
147 if OpenTill >0 then
148 DecTill ;
149 if OpenTill =0 then
150 Customer . Suspend ; // Thread pausieren
151 if OpenTill <10 then KasseHinzu . Enabled := True ;
152 if OpenTill =0 then KasseWeg . Enabled := false ;
153 end ;
154
155 procedure TForm1 . FormClose ( Sender : TObject ; var Action : TCloseAction ) ;
156 var i : integer ;
157 begin
158 for i :=1 to OpenTill do
159 TKassen [ i ]. Terminate ;
160 Customer . terminate ;
161 end ;
162
163 end .

21
8 Literaturverzeichnis
• Bräunl, Thomas: ”Parallel Programmierung. Eine Einführung”. Paderborn: View-
eg, 1993

– Ebenen der Parallelität und Einführung

• Dr. Deilmann, Mario: Parallelwelten“,in entwickler magazin“. S&S: Ausgabe


” ”
Januar/Februar 2007.

– Thread und Aktuallität

• Mey, an Dieter: ”Parallel Programming in MPI. Parallelization Strategies”. Cen-


ter for Computing and Communication Aachen University of Technology, 28.01.2003

– Überblick und Einführung

• Puff, Michael: Thread Programmierung unter Windows mit Delphi“. URL: http://www.luckie-
” ”
online.de“ (Version 2.4)

– Threads unter Delphi. (Empfelung)

• Spieß, Gunnar: ”Proseminar: Paralleles und verteiltes Rechnen. Thema: Parallele


Programmierung in Java

– Verwendung von Threads und Theorie

• Strohal, Martin: Threads“. dsdt.info“.


” ”
– Verwendung von Threads unter Delphi

• Waldschmidt, Klaus: ”Parallelrechner. Architekturen - Systeme - Werkzeuge.”.Leitfäden


der Informatik. Stuttgart, B.G. Teubner Stuttgart, 1995

– Betriebssysteme und Architekturen

• Zilker, Michael: Praxis des Multitasking“. München, Franzis-Verlag GmbH, 1987



– Semaphore

22
Selbständigkeitserklärung
Ich, Marcel Bassüner versichere hiermit, dass ich diese Facharbeit selbständig verfasst
habe und keine anderen als die angegebenen Quellen benutzt wurden, sowie Zitate
kenntlich gemacht wurden.

Unterschrift Ort, Datum

23

You might also like