919
32.7
Synchronisation
#8: Die Anzahl der Tweets der gesuchten Gender-Gruppe.
#9: Die Anzahl der Tweets der gesuchten Gender-Gruppe, in denen der Suchbegriff vor-
kommt.
Das Programm liefert folgende Ausgabe:
Die Bearbeitungszeit können Sie verringern, indem Sie die Datenmenge, die einem Prozess
zur Verarbeitung zugeteilt wird (
chunksize), vergrößern. Ersetzen Sie Zeile #6 der Skripts
durch folgende Zeile:
Sie werden feststellen, dass das Programm jetzt schneller läuft.
32.7 Synchronisation
Manchmal nutzen mehrere nebenläufige Prozesse eine gemeinsame Ressource, z.B. die Stan-
dardausgabe bei einem
print()-Befehl. Das kann zu Konflikten führen. Sie kennen das
Problem aus dem Alltag. In einem Büro teilen sich mehrere Personen eine Espressoma-
schine. Stellen Sie sich das Chaos vor, wenn mehrere Personen versuchen würden, sich
gleichzeitig einen Espresso zu machen. Stattdessen läuft es so ab: Wenn Tom zur Espresso-
maschine geht, reserviert er sie damit. Die anderen sehen das. Falls Tina jetzt auch einen
Espresso braucht, wartet sie, bis Tom fertig ist und weggeht. Dann ist die Espressomaschine
wieder frei und Tina kann sie benutzen. Nach diesem Prinzip funktioniert die Synchronisa-
tion von Prozessen mit einem Lock-Objekt (engl. lock = Schloss). Starten wir mit einem
Experiment: Mehrere Prozesse geben etwas auf dem Bildschirm aus, ohne die
print()-
Anweisungen zu synchronisieren.
Skript:
Es wurden insgesamt 6700 Tweets
dieser Gender-Gruppe untersucht.
Das Wort car wurde in 5.1 Prozent aller Tweets dieser
Gender-Gruppe gefunden.
Bearbeitungszeit: 1.848788 Sekunden.
results = p.map(search, data, chunksize=1000)
# print_no_lock.py
from multiprocessing import Process
def f(i): #1
print("Hallo! ")
print("Das ist Prozess", i)
if __name__ == '__main__':
for i in range(10): #2
Process(target=f, args=(i,)).start() #3
Kapitel 32
Parallele Datenverarbeitung
920
Erläuterung:
#1: Diese Funktion soll in nebenläufigen Prozessen ausgeführt werden. Sie gibt zwei Zeilen
auf dem Bildschirm (Standardausgabe) aus.
#2: Eine Schleife, die 10 Mal durchlaufen wird. Die Variable i nimmt die Werte 0, 1, 2, ..., 9
ein.
#3: Hier werden nacheinander zehn Prozesse erzeugt. Sie sollen die Funktion f() ausfüh-
ren und der Funktionsaufruf bekommt als Argument die Nummer
i.
Starten Sie das Programm nicht mit der Entwicklungsumgebung IDLE. Sonst haben die
Prozesse keinen Zugriff auf die Standardausgabe und ihre
print()-Anweisungen funktio-
nieren nicht. Öffnen Sie stattdessen ein Konsolenfenster (bei Windows: das Programm Ein-
gabeaufforderung im Ordner
Zubehör). Wechseln Sie mit cd in den Ordner mit Ihrem
Programm und starten Sie es mit dem Kommando
> python print_no_lock.py
Die Idee ist, dass jeder Prozess zwei Zeilen ausgeben soll. Wenn man Glück hat, klappt das
auch. Manchmal sieht die Ausgabe jedoch ungefähr so aus wie in Abbildung 32.2.
Abb. 32.2: Ausgabe ohne Synchronisation
Hier läuft es an einer Stelle chaotisch (eingekreist). Was ist passiert? Nachdem ein Prozess
die erste Zeile ausgegeben hat, mogelt sich ein anderer Prozess dazwischen und druckt
auch die erste Zeile aus. Um solche Kollisionen zu vermeiden, kann man die Prozesse mit
dem Lock-Mechanismus synchronisieren.
Das ist die Grundidee:
Es gibt einen kritischen Bereich von Anweisungen, in denen auf eine gemeinsame Res-
source zugegriffen wird. Nur ein einziger Prozess darf zu einem bestimmten Zeitpunkt
diese Anweisungen ausführen (im Beispiel ist der kritische Bereich die Benutzung der
Espressomaschine).
921
32.7
Synchronisation
Es wird ein Lock-Objekt erzeugt, das den kritischen Bereich schützen soll. Jeder Prozess
bekommt Zugriff auf dieses Lock-Objekt. Beachten Sie: Es gibt nur ein Lock-Objekt, das
alle Prozesse gemeinsam benutzen. (Im Beispiel ist das Lock-Objekt der Ort, von dem
aus man die Espressomaschine benutzen kann. Jeder kennt die Stelle, aber nur einer
darf hier stehen.)
Bevor ein Prozess den kritischen Bereich ausführt, benutzt er das Lock-Objekt und ruft
die Methode
acquire() auf (engl. to acquire = anfordern). Nun müssen alle Prozesse
warten, die auch
acquire() aufrufen. Kein anderer kann jetzt in den kritischen Bereich.
Er ist für alle anderen gesperrt. (Im Beispiel findet eine Anforderung statt, wenn sich
jemand vor die Espressomaschine stellt.)
Nachdem ein Prozess den kritischen Bereich ausgeführt hat, benutzt er wieder das Lock-
Objekt und ruft die Methode
release() auf (engl. to release = freigeben). Damit ist die
Sperre aufgehoben und der kritische Bereich freigegeben. (Im Beispiel findet eine Frei-
gabe statt, wenn jemand den Bereich vor der Espressomaschine verlässt und damit Platz
für die anderen Mitbenutzer macht.)
Skript:
Erläuterung:
#1: Hier wird das Lock-Objekt l benutzt und der Zugang zum kritischen Bereich angefor-
dert. Falls gerade ein anderer Prozess im kritischen Bereich ist, wartet dieser Prozess.
#2: Hier beginnt der kritische Bereich. Nur ein Prozess kann diese (und die nächste) Anwei-
sung ausführen.
#3: Hier wird das Lock-Objekt wieder freigegeben. Nun kann ein anderer Prozess den kriti-
schen Bereich »betreten«.
#4: Ein Lock-Objekt wird erzeugt. Alle Prozesse benutzen dieses Objekt zur Synchronisa-
tion.
#5: Hier werden zehn Prozesse erzeugt. Sie führen die Funktion f() aus und übergeben als
Argumente das Lock-Objekt
lock und die Zahl i.
# print_lock.py
from multiprocessing import Process, Lock
def f(l, i):
l.acquire() #1
print('Hallo!') #2
print('Das ist Prozess', i)
l.release() #3
if __name__ == '__main__':
lock = Lock() #4
for i in range(10):
Process(target=f, args=(lock, i)).start() #5

Get Python 3 - Lernen und professionell anwenden now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.