Verteilte Systeme CS5001 Th. Letschert TH Mittelhessen Gießen University of Applied Sciences Client-Server-Anwendungen:
Vom passiven (shared state) Monitor zum aktiven Monitor Monitor (Hoare, Brinch-Hansen, 1975) Ein Monitor ist ein Modul / Objekt, in dem die von Prozessen gemeinsam genutzten Daten und ihre Zugriffsprozeduren (oder Methoden) zu einer Einheit zusammengeführt sind. Monitor = Definition als Klasse mit Synchronisation Kapselt Daten mit ihren synchronisierten Zugriffsmethoden Synchronisation: Gegenseitiger Ausschluss Garantiert exklusiven Zugriff (Vermeide race conditions) Java: synchronized / Lock Bedingungssynchronisation Garantiert Zugriff unter den notwendigen Bedingungen (conditional synchronisation) Java: wait/notify, Condition Monitore sind passive Konstrukte Monitor basiert auf gemeinsamen Speicher Aktive Einheiten (Prozesse) greifen auf passive Ressourcen zu, die ihre Zugriffe synchronisieren Seite 2
passiver Monitor: Beispiel Puffer Gegenseitiger Ausschluss Gleichzeitiger Zugriff nicht erlaubt Bedingungssynchronisation Kein Ablegen in vollen Puffer, kein Entnehmen aus leerem Puffer monitor Buffer { Token place; boolean public class Buffer<TokenType> { TokenType place; boolean synchronized void put(tokentype t) throws InterruptedException { while (! empty ) wait(); place = t; notify(); // one call must not interfere with other calls // Pre: empty void put(token t) { place = t; // one call must not interfere with other calls // Pre: not empty Token get() { return place; synchronized TokenType get() throws InterruptedException { while ( empty ) wait(); notify(); return place; shared state Puffer: Konzept shared state Puffer: Implementierung Seite 3
passiver Monitor: Beispiel Puffer public class Main { static enum Token { PING, PONG ; static Buffer<Token> buffer = new Buffer<>(); public static void main(string[] args) { new Thread(new Runnable(){ // Producer public void run() { while (true) { try { buffer.put(token.ping); Thread.sleep(1000); System.out.println(buffer.get()); catch (InterruptedException e) { ).start(); passiver shared state Puffer: Produzent und Konsument als Nutzer new Thread(new Runnable(){ // Consumer public void run() { while (true) { try { buffer.put(token.pong); Thread.sleep(1000); System.out.println(buffer.get()); catch (InterruptedException e) { ).start(); Seite 4
aktiver statt passiver Monitor: Kommunikation statt Synchronisation Gemeinsamer Zustand ist nicht möglich Die Prozesse können nicht auf eine gemeinsame Ressource zugreifen. Die gemeinsame Ressource muss darum einen Server umgewandelt werden mit eigenem Kntrollfluss der die Aufträge als Nachrichten entgegen nimmt und bearbeitet. Aktiver statt passiver Monitor: Kommunikation statt Synchronisation Prozesse ohne gemeinsamen Zustand haben nur Synchronisationsanweisungen sind nicht mehr notwendig, da es keine gemeinsamen Ressourcen gibt: Gegenseitiger Ausschluss ist kein Thema mehr Die korrekte Reihenfolge der Verarbeitung muss aber weiterhin gewährleistet sein: Bedingungssynchronisation ist weiterhin ein Thema Produzent Konsument passiver Puffer Produzent Seite 5 aktiver Puffer Konsument
Aktiver Puffer Puffer als aktiver Monitor : Nachrichten-verarbeitender Prozess Strategie der Umwandlung passiv => aktiv: synchronisierter Aufruf => Nachrichtenart, Empfang Senden einer Antwort wait => Speichern der Nachricht notify => Verarbeitung gespeicherter Nachrichten Verarbeite Nachrichten: Put-Nachricht sende OK-Antwort Get-Nachricht sende Daten-Antwort public class Buffer<TokenType> { TokenType place; boolean synchronized void put(tokentype t) throws InterruptedException { while (! empty ) wait(); place = t; notify(); synchronized TokenType get() throws InterruptedException { while ( empty ) wait(); notify(); return place; Put-Nachricht mit ihrem Sender wird in WS gespeichert verzögerte Get-Nachricht verarbeiten Get-Nachricht mit Sender wird in WS gespeichert verzögerte Put-Nachricht verarbeiten Seite 6
Aktiver Puffer Puffer als aktiver Monitor : Nachrichten-verarbeitender Prozess (Pseudocode) process Buffer { Queue<PutMsg> pendingputs; Queue<GetMsg> pendinggets; Token place; boolean void processpendingget() { if (! pendinggets.empty) { getmsg, sender = pendinggets.remove() send DataMsg(place) to sender processpendingput(); void process(msg msg, sender) { case msg { PutMsg(item) => if (!empty) { pendingputs.enqueue(msg, sender) else { place = msg.item; send OK to sender; processpendingget(); void processpendingput() { if (! pendingputs.empty) { putmsg, sender = pendinggets.remove() send OKMsg to sender place = putmsg.item processpendingget(); GetMsg(item) => if (empty) { pendinggets.enqueue(msg, sender) else { send Data(place) to sender; processpendingput(); do (forever) { reveive msg from sender process(msg, sender) Seite 7
Aktiver Puffer Puffer als aktiver Monitor Daten-Puffer NachrichtenPuffer put get Produzent Puffer Produzent Konsument Seite 8 Puffer Konsument
aktiver Monitor: Zusammenfassung Monitor aktiver Monitor ~> Server-Prozess Monitor-Nutzer ~> Client-Prozesse passiver => aktiver Monitor Klasse => Prozess Aufruf einer Monitor-Prozedur => Empfang einer Nachricht Körper einer Monitor-Prozedur => Verarbeitung der Nachricht, Ergebnis an den Sender Umsetzung Gegenseitiger Ausschluss völlig unproblematisch: Ein sequenzieller Serverprozess => streng sequenzieller Zugriff auf die Ressource Umsetzung Bedingungssynchronisation Der Serverprozess kann nicht (in wait) blockiert werden wait ~> Speichern der Nachricht / ausbleibende Antwort blockiert Client notify ~> Gelegenheit gespeicherte Nachrichten zu verarbeiten Seite 9