Parallel Lua / CSP Tutorial (Stefan Bosse) [5.2024] |
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.12, luaos 1.6.1
# 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)
▸
◼
|
✗
↻
≡
|
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)
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.
foo(1)
ein...
Die Fehlerbehandlung (genau genommen die Behandlung von Ausnahmefehlern und Signalen) erfolgt in einem sequenziellen Programm mit der try .. catch (e) .. end
Anweisung (deterministisch)
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:
▸
◼
|
✗
↻
≡
|
▸
◼
|
✗
↻
≡
|
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()
▸
◼
|
✗
↻
≡
|
Das wichtigste Kommunikationsobjekt für Prozesse ist die Semaphore als ein geschützter Zähler
Es gibt zwei Operationen:
Eine Semaphore wird für Produzenten-Konsumenten Systeme und für die Koordination zwischen Prozessen eingesetzt
Eine Semaphore 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:
▸
◼
|
✗
↻
≡
|
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
▸
◼
|
✗
↻
≡
|
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.
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.
Überprüfe zuvor die Mittelwertberechnung durch direkte Berechnung (in Schleifen):
▸
◼
|
✗
↻
≡
|