IT-Academy Logo
Sign Up Login Help
Home - Programmieren - Parallele Programmierung, Prozesse, Threads



Parallele Programmierung, Prozesse, Threads

Dieser Artikel soll einen Einblick in die Welt der parallelen Programmierung bieten. Dabei wird auf Begriffe wie "Prozesse" und "Threads" eingegangen.


Autor: Patrick Bucher (paedubucher)
Datum: 17-09-2006, 23:56:47
Referenzen: Siehe 'Referenzen' am Ende des Artikels
Schwierigkeit: Fortgeschrittene
Ansichten: 10151x
Rating: Bisher keine Bewertung.

Hinweis:

Für den hier dargestellte Inhalt ist nicht der Betreiber der Plattform, sondern der jeweilige Autor verantwortlich.
Falls Sie Missbrauch vermuten, bitten wir Sie, uns unter missbrauch@it-academy.cc zu kontaktieren.

[Druckansicht] [Als E-Mail senden] [Kommentar verfassen]



Multitasking

Als PC-Benutzer ist man es sich gewohnt, dass mehrere Programme und Dienste zur gleichen Zeit laufen. Für den Anwender ist dieses gleichzeitige Ausführen von Anwendungen eine Selbstverständlichkeit, für Programmierer stellt es eine Menge Arbeit dar.

Man unterscheidet zwei Arten, wie Programme gleichzeitig ausgeführt werden können:
  1. echt gleichzeitig
  2. scheinbar gleichzeitig

Ersteres ist dann der Fall, wenn der Rechner mit mehreren Prozessoren bestückt ist. Das Betriebssystem kann die Aufgaben so auf mehrere Prozessoren verteilen, die Aufgaben werden gleichzeitig von verschiedenen Prozessoren abgearbeitet. Steht dem Betriebssystem jedoch nur ein Prozessor zur Verfügung, so kann er diese nicht wirklich zur gleichen Zeit ausführen. In diesem Falle wird in sehr hoher Frequenz zwischen den Aufgaben umgeschaltet, sodass sich die Wahrnehmung der Parallelität einstellt.

Prozesse und Threads

In der nebenläufigen Programmierung unterscheiden wir zwischen den zwei Ausführungseinheiten Prozess und Thread.

Ein Prozess wird dann gestartet, wenn ein neues Programm ausgeführt werden soll. Jeder Prozess hat seinen eigenen Adressraum, sodass Prozesse sich nicht gegenseitig in die Quere kommen können. Das Betriebssystem stellt jedoch verschiedene Mechanismen zur Verfügung, damit Prozesse untereinander interagieren können.

Ein Thread ist ein Programmfaden, also eine Sequenz von auszuführenden Befehlen. Ein Prozess kann mehrere Threads besitzen. Dabei teilen sich alle Threads eines Prozesses den gleichen Adressraum (jeder Thread besitzt seinen eigenen Stack, sie verwenden jedoch alle den gleichen Heap).

Jeder Prozess und jeder Thread besitzt seine eigene ID. Dies ist eine Nummer, die den Prozess/Thread systemweit und eindeutig identifiziert. Das Betriebssystem hat die Aufgabe, die verschiedenen Prozesse und Threads zu verwalten und ihnen die notwendigen Ressourcen zur Verfügung zu stellen.

Da sich alle Threads eines Prozesses einen gemeinsamen Heap teilen, kann das Betriebssystem hier keine Schutzfunktion übernehmen. Dies ist die Aufgabe des Programmierers. Instanzen sind bekanntermassen auf dem Heap abgespeichert, somit können sie nicht einem bestimmten Thread zugeordnet werden. Threads besitzen jedoch eigene Stacks, von welchen aus Speicherobjekte referenziert werden können. Instanzen sind also nicht an einen Thread, sondern an einen Prozess gebunden. Kann ein Objekt von verschiedenen Threads verwendet werden, ohne dass es dabei zu Inkonsistenzen kommt, so wird das Objekt als threadsicher oder threadsafe bezeichnet, was ein erstrebenswerter Zustand ist.

Kontextwechsel

Die Zuteilung von Ressourcen, insbesondere der CPU, ist eine wichtige Aufgabe eines jeden Betriebssystems. Dabei sollen alle Prozesse genügend Ressourcen erhalten. Es gibt jedoch die Möglichkeit, Prozesse zu priorisieren. So erhalten Prozesse mehr oder weniger Ressourcen. Jeder Thread erhält ein bestimmtes Zeitintervall, in welcher er die CPU beanspruchen darf. Ist diese Zeit abgelaufen, so ist der nächste Prozess an der Reihe und wird ausgeführt. Dieses Konzept nennt man Kontextwechsel.

Bei diesem Kontextwechsel wird entweder auf einen anderen Threads des selben Prozesses, oder auf einen Thread eines anderen Prozesses umgeschaltet. Im ersten Fall ist die Rede von einem Thread-Kontextwechsel. Wird zu einem Thread eines anderen Prozesses gewechselt, so muss auch der Prozess umgeschaltet werden. Diese Prozess-Kontextwechsel sind wesentlich aufwändiger, da auf einen anderen Adressraum umgeschaltet werden muss.

Diese Wechsel werden vom Scheduler übernommen. Ein Scheduler entscheidet, welcher Thread zu welcher Zeit Ressourcen erhält. Dies geschieht unter der Berücksichtigung der Thread-Prioritäten.

Thread-Prioritäten

Prozesse und Threads sind prinzipiell gleichberechtigt, sodass sie alle genügend Ressourcen erhalten. Damit wichtigere Aufgaben etwas schneller ausgeführt werden können, wurde das Konzept der Prioritäten eingeführt. So kann man einem Thread eine höhere oder eine tiefere Priorität zuweisen.

Threads werden vom Scheduler nach ihrer Priorität gruppiert. Nun werden die Threads der Gruppe mit der höchsten Priorität nacheinander abgearbeitet, bis entweder alle Threads beendet sind oder ein Thread eine nicht leere Gruppe mit einer höheren Priorität existiert. Dann wird die Gruppe mit der nächst tieferen Priorität abgearbeitet. Tauchen Threads in einer höheren Prioritätsgruppe auf, so schaltet der Scheduler auf diese Gruppe um.

Innerhalb einer Prioritätsgruppe wird die CPU-Zeit gleichmässig aufgeteilt (Round-Robin-Verfahren). Damit Threads mit einer höheren Priorität nicht die ganze CPU-Zeit in Anspruch nehmen können, kann der Scheduler z.B. einen gelaufenen Thread um eine Prioritätsstufe senken. Der Scheduler kann dabei keine bestimmte Ausführungsreihenfolge garantieren, dies ist Aufgabe des Programmierers.

Achtung: Hierbei handelt es sich nur um eine mögliche Scheduling-Strategie. Es gibt unterschiedliche Scheduling-Strategien mit, welche auf verschiedene Arten implementiert werden können.

Thread-Zustände

Threads können verschiedene Zustände einnehmen. Dabei hat jeder Thread zu jedem Zeitpunkt exakt einen Status. Pro Prozessor kann immer nur ein Thread gleichzeitig ausgeführt werden.

  • Initialized
    • Der Thread wurde initialisiert, jedoch noch nicht gestartet.
  • Ready
    • Der Thread ist bereit zur Ausführung und wartet nur noch darauf, dass er einen Prozessor zugeteilt bekommt. Threads des Zustands Ready sind nach ihrer Priorität gruppiert.
  • Standby
    • Dieser Thread wird gleich Prozessor-Ressourcen erhalten und somit ausgeführt. Nur ein Thread pro Prozessor kann diesen Status besitzen.
  • Running
    • Dieser Thread wird im Moment abgearbeitet, pro Prozessor kann dies genau ein Thread sein.
  • Wait/Transition
    • Threads in diesem Zustand wären eigentlich bereit zur Ausführung (Status Ready), müssen jedoch noch ein bestimmtes Ereignis oder eine bestimmte nicht-Prozessor-Ressource abwarten. Sobald diese Ressourcen frei sind oder das Ereignis eingetreten ist, wechseln sie zurück zum Status Ready.
  • Terminated
    • Der Thread wurde abgearbeitet und ist nun beendet.

Für die Zuteilung der CPU-Ressource werden zur Threads der Zustände Ready und Running berücksichtigt. Die anderen Prozesse müssen zunächst den Zustand Wait erreichen, bevor sie ausgeführt werden können. Die anderen Zustände sind für den Programmierer nicht weiter interessant.

Wechsel von Thread-Zuständen

Ein Thread kann von einem Zustand immer nur in bestimmte andere Zustände geraten. So muss ein Thread zunächst die Zustände Initialized, Ready, Standby und Running durchlaufen haben, bevor er abgeschlossen (also Terminated) sein kann. Dazu ein Beispiel:


  1. Wir initialisieren einen neuen Thread. Dieser hat nun den Status Initialized.
  2. Nun geben wir diesen Thread zur Ausführung frei, sodass er den Status Ready besitzt.
  3. Nun meldet der Scheduler, dass unser Thread als nächstes gestartet werden kann, er wechselt zum Status Standby.
  4. Die CPU ist nun frei, unser Thread erhält den Zustand Running und wird somit ausgeführt.
  5. Nun ist die CPU-Zeit schon wieder abgelaufen, unser Thread wurde aber noch nicht ganz abgearbeitet. Er muss sich also wieder hinten anstellen und erhält so wieder den Status Ready.
  6. Einige Threads werden abgearbeitet, sodass unser Thread als nächstes an die Reihe kommt - Status Standby.
  7. Unser Thread wird nun wieder ausgeführt und erhält den Status Running
  8. Wir warten auf eine bestimmte Ressource/auf ein bestimmtes Ereignis, sodass unser Thread nicht mehr arbeiten kann. Der Thread wird zurückgestellt und erhält den Zustand Wait/Transition.
  9. Das besagte Ereignis ist eingetreten, der Scheduler weckt unseren Thread wieder und vergibt ihm erneut den Status Ready.
  10. Unser Thread durchläuft erneut die Stati Standby und Running.
  11. Dieses mal hat die CPU-Zeit ausgereicht, um alle Aktionen durchzuführen. Der Thread ist abgearbeitet und erhält somit den Status Terminated

Was hier nach einer mühsamen und langen Prozedur aussieht, geschieht auf modernen Rechnern mehrmals pro Sekunde!

Referenzen

  • Wikipedia
  • Parallele Programmierung mit Java Threads
    • Rainer Oechsle
    • Fachbuchverlag Leipzig
    • ISBN:3-446-21780-0


Goergi
Developer
Beitrag vom:
08-10-2006, 18:48:12

Das Bild...

... ist jetzt da!

-----------------------------------------------------


paedubucher
Professonial
Beitrag vom:
19-09-2006, 11:12:10

Bild fehlt

Im Abschnitt "Wechsel von Thread-Zuständen" hat es einen grösseren Abstand, da würde eine Grafik hineingehören.
Dabei habe ich mich ja extra an den Windows PC gesetzt und den IE gestartet, um das Bild hochzuladen. Welch sinnloses Martyrium :-D

Könnte man das bei Gelegenheit noch fixen? Danke!

-----------------------------------------------------


[back to top]



Userdaten
User nicht eingeloggt

Gesamtranking
Werbung
Datenbankstand
Autoren:04505
Artikel:00815
Glossar:04116
News:13565
Userbeiträge:16552
Queueeinträge:06241
News Umfrage
Ihre Anforderungen an ein Online-Zeiterfassungs-Produkt?
Mobile Nutzung möglich (Ipone, Android)
Externe API Schnittstelle/Plugins dritter
Zeiterfassung meiner Mitarbeiter
Exportieren in CSV/XLS
Siehe Kommentar



[Results] | [Archiv] Votes: 1140
Comments: 0