»Don't interrupt me while I'm interrupting« – Winston S. Churchill
18 Parallele Programmierung
Dieses Kapitel wird Sie in die Programmierung mit sogenannten Threads einführen, die es ermöglichen, mehrere Aufgaben gleichzeitig auszuführen. Bevor wir allerdings mit den technischen Details und Beispielprogrammen beginnen können, müssen einige Begriffe eingeführt werden, und Sie müssen die prinzipielle Arbeitsweise moderner Betriebssysteme verstehen.
18.1 Prozesse, Multitasking und Threads 

Im Folgenden werden die Begriffe Programm und Prozess synonym für ein laufendes Programm verwendet.
Wir sind als Benutzer moderner Computer gewohnt, dass ein Rechner mehrere Programme gleichzeitig ausführen kann. Beispielsweise schreiben wir eine E-Mail, während im Hintergrund das letzte Urlaubsvideo in ein anderes Format umgewandelt wird und eine MP3-Software unseren Lieblingssong aus den Computerlautsprechern ertönen lässt. Abbildung 18.1 zeigt eine typische Arbeitssitzung, wobei jeder Kasten für ein laufendes Programm steht. Die Länge der Kästen entlang der Zeitachse zeigt an, wie lange der jeweilige Prozess läuft.
Abbildung 18.1 Mehrere Prozesse laufen gleichzeitig ab
Faktisch kann ein Computer aber nur genau eine einzige Aufgabe zu einem bestimmten Zeitpunkt übernehmen und nicht mehrere gleichzeitig. Selbst bei modernen Prozessoren mit mehr als einem Kern oder bei Rechnern mit vielen Prozessoren ist die Anzahl der gleichzeitig ausführbaren Programme durch die Anzahl der Kerne bzw. Prozessoren beschränkt. Wie ist es also möglich, dass das einleitend beschriebene Szenario auch auf einem Computer mit nur einem Prozessor, der nur einen einzigen Kern besitzt, funktioniert?
Der dahinter stehende Trick ist im Grunde sehr einfach, denn man versteckt die Limitierung der Maschine geschickt vor dem Benutzer, indem man ihm vorgaukelt, es würden mehrere Programme simultan laufen. Dies wird dadurch erreicht, dass man jedem Programm ganz kurz die Kontrolle über den Prozessor zuteilt, es also laufen lässt. Nach Ablauf der sogenannten Zeitscheibe wird dem Programm die Kontrolle wieder entzogen, wobei sein aktueller Zustand gespeichert wird. Nun kann dem nächsten Programm eine Zeitscheibe zugeteilt werden. In der Zeit, in der ein Programm darauf wartet, eine Zeitscheibe zugeteilt zu bekommen, wird es als schlafend bezeichnet.
Man kann sich die Arbeit eines Computers so vorstellen, dass in rasender Geschwindigkeit alle laufenden Programme geweckt, für eine kurze Zeit ausgeführt und dann wieder schlafen gelegt werden. Durch die hohe Geschwindigkeit des Umschaltens zwischen den Prozessen wird dies vom Benutzer nicht wahrgenommen. Die Verwaltung der Prozesse und ihrer Zeitscheiben wird von den modernen Betriebssystemen übernommen, die deshalb auch Multitasking-Systeme (dt. Mehrprozessbetriebssysteme) genannt werden.
Die korrekte Darstellung unseres anfänglichen Beispiels müsste also eher wie in Abbildung 18.2 gezeigt aussehen. Dabei symbolisiert jedes kleine Kästchen innerhalb des Blocks »Reale Prozessorbelegung« eine Zeitscheibe:
Abbildung 18.2 Die Prozesse wechseln sich ab und laufen nicht gleichzeitig
Innerhalb eines Prozesses selbst kann aber weiterhin nur eine Aufgabe zur selben Zeit verarbeitet werden, da das Programm linear abgearbeitet wird. In vielen Situationen ist es aber erforderlich, dass ein Programm mehrere Operationen zeitgleich durchführt. Beispielsweise sollte die Benutzeroberfläche während einer aufwendigen Berechnung nicht blockieren, sondern den aktuellen Status anzeigen, und der Benutzer sollte die Berechnung gegebenenfalls abbrechen können. Ein anderes Beispiel ist ein Webserver, der während der Verarbeitung einer Client-Anfrage auch noch für weitere Zugriffe verfügbar sein muss.
Es ist zwar möglich, die Beschränkung auf nur eine Operation zur selben Zeit durch die Erzeugung weiterer Prozesse zu umgehen, aber um Daten zwischen verschiedenen Prozessen auszutauschen, muss relativ viel Aufwand getrieben werden, weil jeder Prozess seine eigenen Variablen hat, die von den anderen Prozessen abgeschirmt sind.
Eine befriedigende Lösung für das Problem liefern sogenannte Threads. Ein Thread (dt. Faden) ist ein Ausführungsstrang innerhalb eines Prozesses. Standardmäßig besitzt jeder Prozess genau einen Thread, der eben die Ausführung des Prozesses organisiert.
Nun kann ein Prozess aber auch mehrere Threads starten, die dann durch das Betriebssystem wie Prozesse scheinbar gleichzeitig ausgeführt werden. Der Vorteil von Threads gegenüber Prozessen besteht darin, dass sich die Threads eines Prozesses denselben Speicherbereich für globale Variablen teilen. Wenn also in einem Thread eine globale Variable verändert wird, ist der neue Wert auch sofort für alle anderen Threads des Prozesses sichtbar. [Um Fehler zu vermeiden, müssen solche Zugriffe in mehreren Threads speziell mit sogenannten Critical Sections abgesichert werden. Wir werden diese Thematik im Laufe des Kapitels noch ausführlicher behandeln. ] Demgegenüber hat jeder Thread seine eigenen lokalen Variablen. Außerdem ist die Verwaltung von Threads für das Betriebssystem weniger aufwendig als die Verwaltung von Prozessen. Deshalb werden Threads auch Leichtgewichtprozesse genannt.
Die Threads in einem Prozess kann man sich folgendermaßen vorstellen:
Abbildung 18.3 Ein Prozess mit drei Threads
Nach dieser theoretischen Einführung werden wir uns der Programmierung mit Threads in Python zuwenden.







bestellen





