Alles über Prozesse, Threads und Hyperthreading

Definitionen und Erklärungen zur Funktionsweise von Prozessoren

Die Begriffe Threads, Prozesse, Hyperthreading hat wohl jeder schon einmal gehört. Aber so richtig trennscharf werden die Begriffe nicht immer verwendet. In diesem Beitrag möchte ich Dir gerne den Unterschied erklären.

Prozessmodell

Ein Prozess ist ein Vorgang, der Berechnungen auf einem Prozessor ausführt, eine Instanz eines Programms.
Ein Prozess hat dabei Zugriff auf die Ressourcen des Prozessors. Aber nicht nur das, er umfasst neben der Befehlsabfolge einen Adressbereich im Arbeitsspeicher und auf dem Stack-Speicher.
Da ein Prozessor(kern) nur einen Prozess gleichzeitig bearbeiten kann, ein Betriebssystem mit laufenden Anwendungen aber eine ganze Reihe von laufenden Prozessen benötigt, gibt es einen sog. Scheduler, der anhand eines Algorithmus entscheidet, welcher Prozess zu welchem Zeitpunkt den Prozessor nutzen darf.
Es konkurrieren also alle Prozesse um die Zeit auf dem Prozessor.
Ein Programm wird also genau genommen nur dann ausgeführt, wenn es sich auf dem Prozessor befindet. Wird ein Prozess durch den Scheduler von der CPU genommen und durch einen anderen Prozess ersetzt, so wird der vorige Prozess eingefroren (die Registerbelegung, ein Speicherabbild, etc. gespeichert) und in eine Queue eingereiht. Der Prozess muss nun warten, bis er vom Scheduler wieder auf die CPU gelassen wird.
Diese Abwechslung von Prozessen findet sehr schnell statt, sodass man in der Regel nicht mitbekommt, wenn ein Prozess warten muss. Im Ernstfall würde ein Programm mit lange eingefrorenem Prozess aber nicht reagieren.

Der Scheduler

Der Scheduler ist Teil des Betriebssystems und dafür verantwortlich, welcher Prozess wieviel Zeit auf dem Prozessor verbringen darf. In der Regel führt ein Scheduler Listen, anhand derer er entscheidet, welcher Prozess als nächstes den Prozessor nutzen darf.
Die Arbeit des Schedulers ist entscheidend dafür, ob Programme als flüssig ablaufend wahrgenommen werden, oder zwischendurch stocken.
Der Scheduler unterscheidet Prozesse mit 3 Stati:
1.) Laufend – Der Prozess, der sich gerade auf der CPU befindet.
2.) Bereit – Prozesse, die alle Ressourcen zur Verfügung haben, um weiter berechnet zu werden
3.) Wartend – Prozesse, die auf andere Ressourcen (z.B. Daten von der Festplatte) warten
Moderne Prozessoren unterstützen zudem Interrupts. Andere am Programmablauf beteiligte Komponenten können durch Interrupt-Signale die Abarbeitung eines Prozesses auf der CPU stoppen. Der vom Scheduler als nächstes geplante Prozess übernimmt dann den „Platz“ auf der CPU.

Abbildung 1 – Prozessmodell, eigene Darstellung

Abbildung 1 zeigt schematisch dargestellt, wie Prozesse vom Scheduler gemanaged werden. Jeder Prozess kriegt vom Scheduler die Ressourcen zugeteilt. Die CPU und I/O Geräte, z.B die Festplatte oder Laufwerke, kann nur ein Prozess gleichzeitig nutzen. Der Arbeitsspeicher kann mittels Speicheradressen aufgeteilt und “gleichzeitig” von mehreren Prozessen verwendet werden.

Threadmodell

Das Prozessmodell setzt voraus, dass jeder Prozess einem einzigen “Ablauf”, auch Trace genannt, folgt.
Das Thread-Modell setzt diese Annahme aus und ermöglicht mehrere “Ausführungsfäden” (“Threads”) in einem Prozess. Dadurch entsteht ein besonderer Unterschied: Während Prozesse jeweils einen eigenen Adressraum im Speicher haben und I/O Ressourcen jeweils nur einem Prozess gleichzeitig zur Verfügung stehen können, teilen sich Threads einen Adressraum und können prinzipiell auf die gleichen Ressourcen zugreifen.
Das heißt, ein Thread hat Zugriff auf den Speicherbereich eines anderen Threads, sofern diese im gleichen Prozess ablaufen.
Man könnte nun davon ausgehen, dass Threads ein Sicherheitsrisiko darstellen. Da Threads allerdings nur den Speicherbereich innerhalb eines Prozesses einsehen können, und ein Prozess immer von nur einem Nutzer ausgeführt wird, ist hier nicht mehr Zugriff auf Speicherbereiche möglich, als innerhalb eines Prozesses.
Der gemeinsame Speicher bei Threads stellt einen großen Vorteil dar, da Threads untereinander so sehr effizient kommunizieren können, einfach indem Speicheradressen übergeben werden.

Die Verwendung von Threads und dem damit verbundenen gleichen Speicherbereich, birgt aber auch Risiken.
So kann ein Thread den Speicherbereich verändern, während ein anderer Thread gerade pausiert. Der zweite Thread findet dann, wenn er weiter arbeiten möchte eine andere Situation vor, als vorher. Das Problem lässt sich besonders an globalen Variablen erkennen. Nehmen wir an, wir haben eine globaler Variable int Kontostand = 100. Der erste Thread führt nun eine Berechnung (in diesem Fall eine Überweisung von 80€ aus) und überprüft, ob genügen Geld vorhanden ist. Er liest, dass der Kontostand = 100 ist und beginnt dann mit der Ausführung der Überweisung. Während der erste Thread noch rechnet, greift der zweite Thread auf den Kontostand zu und möchte ebenfalls eine Überweisung über 70€ durchführen. Er liest, dass mit 100€ noch ausreichend Geld auf dem Konto ist und beginnt die Überweisung. Der erste Thread ist nun fertig und setzt die globale Variable Kontostand auf 20. Mit diesem Wert hätte der zweite Thread niemals mit der Überweisung beginnen dürfen.
Programme, die mittels Threads parallelisiert werden, dürfen also niemals unüberlegt globale Variablen nutzen.

Abbildung 2 – Threadmodell, eigene Darstellung

Abbildung 2 zeigt schematisch, wie mehrere Threads (“Fäden”) innerhalb eines Prozesses (Prozessraumes) ablaufen.

Thread-Safety

Programme, die problemlos innerhalb mehrere Threads ablaufen können, werden Thread-Safe genannt. Programme, die z.B. wie im oberen Beispiel auf globale Variablen zurückgreifen sind nicht thread-safe.

Hyperthreading

Hyperthreading setzt zwar vom Prinzip bei der gleichen Problemstellung an, aber auf einer ganz anderen Ebene. Hyperthreading ist eine Technologie, die in Prozessoren, also auf Hardwareebene zum Einsatz kommt. Sie unterscheidet sich damit grundsätzlich von Threads auf Prozessebene (ein Softwareentwickler hat beispielsweise keinerlei Einfluss auf Hyperthreading, während er die Verwendung von Threads und Prozessen steuern kann).
Der Prozessor erzeugt hierbei virtuelle Prozessoren, auf die Prozesse aufgeteilt werden. Dadurch wird eine bessere Auslastung der Prozessorressourcen erreicht.

Rückmeldungen