Übung 5: Parallel Lua / CSP Tutorial (Stefan Bosse) [6.2025] |
In dieser Übung findet eine erste Einführung in die Programmierung von parallelen Systemen nach dem CCSP Modell. Dabei steht CCSP für "Concurrent Communicating Sequential Processes" und beschreibt die Komposition und Kommunikation von sequenziellen Prozessen mit Konkurrenz. man unterscheidet:
Minimalanforderungen: lvm 1.1.18 luv 2.7.5!
# lvm myprog.lua
# lvm weblvm.lua
Service thread 2 started.
[2@1592562880] HTTP server (2) listening to http://0.0.0.0:4610
Monitor thread 4 started.
[4@1592562880] HTTP server (4) listening to http://0.0.0.0:4611
▸
◼
|
✗
↻
≡
|
Sequenzielle Prozesse werden in dem gleichen Thread und der gleichen Koroutine ausgeführt.
Wichtig: Die Seq-Teilprozesse besitzen einen eigenen eingeschränkten Kontext; Bindungen von freien Variablen sind nicht möglich (also außerhalb der Funktion definierte), nur über den geteilten Kontext!
Sequenzielle Prozesse sind in Lua (und jeder anderen imperativen/prozeduralen Programmiersprache) ein Artefakt (jedes Programm und jede Funktion ist bereits ein seq. Prozess)
▸
◼
|
✗
↻
≡
|
Aufgabe 1. Füge in die beiden obigen seq. Prozesse die sleep(millisec) Funktion vor der Konsolenausgabe print ein (wähle für die Verzögerung Werte im Bereich 100-500 ms)
Frage 2. Wie verhält sich das Programm? Füge die Ausgabe der Prozesszeit ein (print(time())
) vor und nach dem sleep Aufruf. Alternativ kann anstelle der print die log Anweisung verwendet werden. Diese gibt die aktuelle Prozesszeit in Millisekunden zusammen mit den Argumenten aus.
Frage 3. Was passiert wenn in einem Prozess ein Fehler auftritt? Also füge z.B. den Aufruf einer nicht definierten Funktion foo(1)
ein...
Die Fehlerbehandlung (genau genommen die Behandlung von Ausnahmefehlern und Signalen) erfolgt in einem sequenziellen Programm mit der funktionalen Try().catch().Finally()
Anweisung (deterministisch)- lvm unterstützt eine nicht Lua-konforme try .. catch(e) .. end
Kurzschreibweise.
Die Fehlerbehandlung in parallelen Systemen ist nicht ohne weiteres möglich!
Alle Prozesskonstruktoren werden dann mit einem Fehlersignal "EPROC" abgebrochen (außer es handelt sich um ein Signal):
Benutze nur noch die Großschreibung Try().catch().Finally()
um mit der generischen und Fengari Lua VM kompatibel zu sein.
▸
◼
|
✗
↻
≡
|
debug=true
erreicht werden:
▸
◼
|
✗
↻
≡
|
▸
◼
|
✗
↻
≡
|
Koroutinen werden im gleichen Kontext ausgeführt. Jedoch führt die CSP Bibliothek einen lokalen geteilten Kontext ein (indem hier z.B. die print Funktion fehlt und daher durch den geteilten Kontext explizit eingeführt werden muß).
Die CSP Bibliothek stellt verschiedene Prozesskommunikationsmoethoden zur Verfügung. Dabei muss zwischen den Koroutinen (Fibers) und den parallelen Prozessen (Threads) unterschieden werden. I.a. gibt es ein zusätzliches boolesches Argument bei der Erzeugung der Kommunikationsobjekte. Ein Beispiel eines Events ist nachfolgend geziegt.
Events haben zwei Methoden:
▸
◼
|
✗
↻
≡
|
Frage 4. Ist der Co Prozess synchron? D.h. wird auf die Terminierung aller Teilprozesse gewartet?
Aufgabe 5. Verschiebe die sleep Operation aus Prozess 1 in Prozess 2 (Kommentar entfernen und oben einfügen). Welches Problem kann bei der Verwendung eines Eventobjekts entstehen?
▸
◼
|
✗
↻
≡
|
Frage 6. Was passiert im Prozessfluss wenn der Par Konstruktor mit Fork ausgetauscht wird?
Ein Channel wird zwischen mindestens zwei Prozessen verwendet um synchronisiert Daten auszutauschen
Die Daten werden automatisch serialisiert und wieder deserialisiert
Ein Channel wird mit dem Konstruktor Channel(n) erzeugt. Die Puffergröße n gibt die maximale Anzahl zwischengespeicherter Daten an.
Ein Channel mit n=0 kann nur von zwei Prozessen (einem Leser und einem Schreiber) verwendet werden → Rendezvous Protokoll
Es gibt zwei Operationen: channel:write(data) und channel:read()
Frage 7. Was läuft bei dem nächsten Beispiel falsch (beachte die Puffergröße vom Komm.kanal)?
Hinweis: Wenn der Par Prozess nicht terminiert, benutze den ◼ Knopf bis die Meldung mit dem Signal INTR erscheint! Ggfs. Konsolenupdate durchführen (rechts, Kreispfeil)
Aufgabe 8. Ändere das Programm so ab dass sich beide Prozesse richtig synchronisieren und der Par Prozess terminiert.
▸
◼
|
✗
↻
≡
|
Das wichtigste Kommunikationsobjekt für Prozesse ist der Semaphor als ein geschützter Zähler
Es gibt zwei Operationen:
Ein Semaphor wird für Produzenten-Konsumenten Systeme und für die Koordination zwischen Prozessen eingesetzt
Ein Semaphor wird mit dem Konstruktor Semaphore(init) erzeugt. Der Startwert gibt den initialen Zählerwert an → die richtige Wahl ist relevant und bestimmt die Anwendung!
Im nächste Beispiel werden Semaphoren für die Synchronisation von parallel ausgeführten Arbeitsprozessen (Worker) mit einem Leitungsprozess (Master) verwendet. Es gibt bei der parallelen und verteilen Datenverarbeitung zwei wesentliche Phasen:
▸
◼
|
✗
↻
≡
|
Frage 9. Wie ist der zeitliche Ablauf?
Frage 10. Welchen Wert haben die Semaphorenzähler von prod und cons am Ende?
Aufgabe 11. Verändere das Prozesssystem derart dass der Master außerhalb des (nach dem) Worker Pool (also im Hauptprozess) ausgeführt wird. Dazu muss der Fork Prozesskonstruktor verwendet werden. Warum?
Das DP Problem dient als klassisches Deadlock Problem und das Auftreten von Race Conditions (Wettrennen zwischen Threads) bei geteilten Ressourcen mit konkurrierenden und nebenläufigen (asynchronen) Zugriff
▸
◼
|
✗
↻
≡
|
Aufgabe 12. Starte eine Reihe (10) von Durchläufen. Ist die Ablaufreihenfolge deterministisch?
Aufgabe. Führe in jedem Phil.prozess eine Zählschleife (for)(1,M) ein die den obigen Anweisungblock (Block) kapselt. Experimentiere mit den auskommentierten random delay (sleep in Millisekunden) und der Threadwechsel mit yield. Führe den Prozess aus, mit z.b. M=20 Durchläufen. Was lässt sich beobachten?
Aufgabe 14. Erweitere nun das parallele Prozesssystem auf N=5 Phils. Lässt sich ein Deadlock feststellen? Die Versuche müssen ggfs. mehrfach wiederholt werden (und nur auf Rechnern mit mehr als einer CPU/Core ist etwas auffälliges zu beobachten).
Frage 15. Was passiert hier? Was führt zum Versagen des parallelen Systems?
Frage 16. Wie kann das Versagen des Systems naiv verhindert werden?
In der folgenden Aufgabe soll ähnlich wie den parallelen Zellulären Automaten eine Matrixberechnung auf vier Prozesse verteilt und partitioniert werden. Dabei gibt es zwei Phasen:
mit |M|: Anzahl der Elemente der Matrix M.
Aufgabe 17. Implementiere im nachfolgenden Programmcode die Verteilungs- und Zusammenführungsphase der vier Arbeiterprozess durch einen Master(prozess) gemäß obigen Produzenten-Konsumenten Beispiel mit Semaphoren. Beide Phasen der Berechnung sind getrennt auszuführen.
Aufgabe 18. Überprüfe zuvor die Mittelwertberechnung durch direkte Berechnung (in Schleifen):
▸
◼
|
✗
↻
≡
|