Communicating Sequential Processes: Entwurfsmuster mit Kanälen und Rendezvous

Parallele Programmierung (3IB)

Prof. Dr.-Ing. Sandro Leuchter
Hochschule Mannheim, Fakultät für Informatik
Wintersemester 2024/2025

 

Dieses Werk ist lizenziert unter einer Creative Commons „Namensnennung – Nicht-kommerziell – Weitergabe unter gleichen Bedingungen 4.0 International“ Lizenz.

Überblick

  • Exchanger<T>
  • BlockingQueue<T>
    • Erzeuger/Verbraucher Design Pattern
    • Filter-Verkettung
  • Channel-Konzept in Go
  • Logging

Rendezvous-Punkte mit der Exchanger-Klasse

Exchanger: Synchroner Austausch

  • synchroner, typisierter Austauschkanal zwischen zwei Teilnehmern
  • zeitgleicher Austausch von Objekten zwischen zwei Threads
  • erster am Rendezvous-Punkt (hier t1) wird angehalten, bis Partner geliefert hat
T data1, data2, x; /* … */
var e = new Exchanger<T>();
x = e.exchange(data1);

Koordinierter Datenaustausch über BlockingQueue

“Queue”-Interfaces

Queue-Interfaces1

Methoden von BlockingQueue<E>

mit Exception nicht blockierend blockierend Timeout
einfügen add(e) offer(e) put(e) offer(e, time, unit)
auslesen remove() poll() take() poll(time, unit)
prüfen element() peek() nicht definiert nicht definiert

Hettel und Tran (2016, 145 (modifiziert))

Implementierungen

Implementierungen1

Entkopplung bei Erzeuger/Verbraucher

  • komplette Entkopplung bei asynchronen Methoden (offer()/poll())
  • Synchronisierung von Erzeuger/Verbraucher mit blockierenden Methoden (put()/take())

Hettel und Tran (2016, 146)

mehrere Erzeuger/Verbraucher

  • im Gegensatz zum Exchanger können beliebig viele Erzeuger und Verbraucher auf die Queue zugreifen

Hettel und Tran (2016, 150)

Benutzungsmuster: Filterverkettung

  • verbreitetes Muster auf der Basis Erzeuger/Verbraucher
  • auf jedem Level können Filter, Map” oder Reduce ablaufen

Hettel und Tran (2016, 149)

sequentielle Verarbeitung:

  • 1 Core benötigt

Benutzungsmuster: Filterverkettung

  • alle Verarbeitungsstufen arbeiten parallel

Hettel und Tran (2016, 149)

Parallelisierung:

  • 3 Cores benötigt

Benutzungsmuster: Filterverkettung

  • vertikale Skalierung: Level 2 erhält zusätzliche Instanzen

Hettel und Tran (2016, 150)

Skalierung:

Benutzungsmuster: Filterverkettung

  • im Beispiel reichen zwei Instanzen für Level 2

Hettel und Tran (2016, 150 (modifiziert))

Optimierung:

  • 4 Cores benötigt

Kanäle in Go

Go-Syntax nicht prüfungsrelevant

Communicating Sequential Processes1

  • Kanal
    • atomare Operationen zum Senden und zum Empfangen
    • Channel-Deklaration: Typbindung
  • Empfang von Nachrichten
    • blockiert solange keine Nachricht im Kanal verfügbar ist
  • Senden von Nachrichten
    • synchroner Botschaftenaustausch (“Rendezvous”)
      • Kanal puffert Nachrichten nicht, Sender wird solange blockiert, bis Nachricht abgeholt wurde
    • asynchroner Botschaftenaustausch
      • Kanal hat Queue mit endlicher Kapazität > 0
      • wenn Kapazität noch nicht erreicht ist, wird der Sender nicht blockiert
      • wenn die Kapazität erreicht ist, wird der Sender blockiert, bis die Queue wieder Platz hat und die Nachricht dort eingereiht wurde

Essentielle Go-Syntax: Kanäle und Co-Routinen

  • c <- a
    Senden von Inhalt von Variable a (oder anderem Ausdruck) als Nachricht über Kanal c
  • a <- c
    Nachricht von Kanal c empfangen und Variable a zuweisen (man kann auch (<- c) als Ausdruck verwenden)
  • go fun(...)
    asynchroner Funktionsaufruf von fun(...)
    • blockiert nicht, läuft als Co-Routine (“Go-Routine”) ab
  • selektives Warten auf mehrere Channels, normalerweise in for-Schleife
    • default: falls keine Nachricht an den anderen Kanälen anliegt
      select {          
          case x <- c1: 
              ...x...       
          case <-quit:
              ...
      default:
          ...
      }

Beispiel-Programm in Go

package main
var
1    quit chan bool
func f() {
    // ... B
3    quit <- true
}
func main() {
2    quit = make (chan bool)
    // ... A
    go f()       // fork B
    // ... C
4    <-quit       // wait B
    // ... D
}
1
Deklaration des Kanals quit für Elemente vom Typ bool
2
Initialisierung des Kanals quit für Elemente vom Typ bool mit make
make (chan T, kapa) erzeugt Kanal für Typ T mit Kapazität kapa (int)
3
hier synchrone Kommunikation true in quit schreiben
(Kapazität kapa des Kanals ist hier 0)
4
aus quit lesen, blockiert bis Wert verfügbar
  • Ausführungsreihenfolge: A; (B | C); D

Laboraufgabe “CSP mit Java-Mitteln”

  • Projekt: pp.10.01.Rendezvous
  • Bearbeitungszeit: 20 Minuten
  • Musterlösung: 10 Minuten
  • Kompatibilität: mindestens Java SE 10

Logging

Aufgaben beim Logging

  • Ausgabe/Speichern von Nachrichten während des (operativen) Betriebs von Systemen.
  • Nebenaufgabe: Soll möglichst wenig belasten
  • RFC1 5424 (Gerhards (2009)): Severity-Levels (Emergency – Alert – Critical – Error – Warning – Notice – Informational – Debug)
  • In verteilten Systemen: Herausforderung Timing (Uhren auf Knoten können unterschiedlich gehen), Herstellen von Bezügen zwischen Meldungen unterschiedlicher Knoten
  • Zentrale Komponente; mehrere Puffer, damit loggende Threads sich nicht wegen der nebenläufigen Nutzung des Pufferspeichers gegenseitig blockieren

Laboraufgabe “Logging in Multi-Threadumgebungen”

  • Projekt: pp.10.02-Logger
  • Bearbeitungszeit: 35 Minuten
  • Musterlösung: 25 Minuten
  • Kompatibilität: mindestens Java SE 19

Referenzen

Gerhards, Rainer. 2009. The Syslog Protocol. Request for Comments. RFC 5424; RFC Editor, März. doi:10.17487/RFC5424,.
Hettel, Jörg und Manh Tien Tran. 2016. Nebenläufige Programmierung mit Java. Konzepte und Programmiermodelle für Multicore-Systeme. Heildelberg: dpunkt.verlag.
Hoare, C. A. R. 1978. Communicating Sequential Processes. Commun. ACM 21, Nr. 8 (August): 666–677. doi:10.1145/359576.359585,.