Seminar

Objekt-Orientierte Parallele Programmierung

SS 95

Das Actor- und das ActorSpace-Modell

Frank Thilo


Inhaltsverzeichnis


1 Übersicht

Das Thema dieser Abhandlung sind das Actor- und das ActorSpace-Modell. Beide stellen grundlegende Konzepte bereit, um parallele, verteilte Systeme aus einzelnen Objekten aufzubauen. Diese Objekte interagieren dabei über den Austausch von Nachrichten.

Zuerst wird das Actor-Modell behandelt. Hierbei wird auf die Grundlagen, die einzelnen Operationen und zur Vertiefung auf Möglichkeiten der bildlichen Darstellung eingegangen. Den Abschluß bildet schließlich ein einfaches Beispiel.

Es folgt eine Betrachtung des weitergehenden ActorSpace-Modells mit einer grundlegenden Behandlung der neuen Konzepte, die durch neue und erweiterte Operationen ermöglicht werden.


2 Das Actor-Modell

In diesem Modell besteht ein paralleles System aus autonomen Objekten, den sogenannten Aktoren. Es handelt sich hierbei um aktive Objekte, die selbständig Aktionen ausführen können (in realen Implementierungen könnten ihnen eigene Threads zugewiesen sein), im Gegensatz zu den passiven Objekten, auf denen objektorientierte Programmiersprachen wie Eiffel und C++ basieren.

Aktoren sind vollständig gekapselt, d.h. ihr interner Zustand ist von außen weder direkt sichtbar noch manipulierbar. Die einzige Möglichkeit, einen bestehenden Aktor zu beeinflussen, besteht darin, ihm eine Nachricht zu senden, worauf der Empfänger dann mit verschiedenen Aktionen reagieren kann. Dazu besitzt jedes Objekt eine eindeutige, statische Mailadresse mit zugehöriger Mailqueue sowie ein Verhalten (behaviour), das seinen inneren Zustand darstellt. (Wie später deutlich wird, kann ein einzelnes Objekt in Form mehrerer simultan existierender actor machines verschiedene Verhalten aufweisen).

2.1 actor primitives

Trifft nun eine Nachricht bei einem Aktor ein, so kann dieser darauf mit dem Ausführen von Operationen reagieren. Diese grundlegenden Operationen, die ihm zur Verfügung stehen, heißen actor primitives. Es gibt drei solcher Primitiven: send to, become und create, die im Folgenden besprochen werden.

2.1.1 send to

Mit send to kann ein Objekt einem anderen eine Nachricht schicken. Als Parameter erhält diese Operation die Adresse des Empfängers sowie die zu versendende Nachricht selbst. Das Zielobjekt wird über dessen eindeutige Mailadresse bestimmt, welche dem Sender bekannt sein muß. Hierbei kann man drei Möglichkeiten unterscheiden:

Die Adresse war dem Aktor schon vor Abarbeitung der aktuellen Nachricht bekannt, ist also ein Teil seines aktuellen inneren Zustands.

Die Adresse wurde dem Aktor soeben als ein Teil der Nachricht mitgeteilt.

Der Sender hat den Adressaten soeben selbst mittels create erzeugt und somit dessen Adresse erhalten.

Zu beachten ist, daß der Nachrichtenversand asynchron ist, der Sender also weder auf die Empfangsbereitschaft des Empfängers noch auf die Abarbeitung der Nachricht warten muß. Statt dessen wird vom Mailsystem garantiert, daß die abgeschickte Nachricht in die Mailqueue des Adressaten eingefügt wird. Hingegen wird über die Reihenfolge des Eintreffens keinerlei Aussage gemacht, so daß es möglich wäre, daß eine "später" abgeschickte Nachricht in der Mailqueue weiter vorne eingefügt wird. Daß hier keine Einschränkung getroffen wird, kann sich u.a. positiv auf die Effizienz des Nachrichtenversands auswirken (man denke beispielsweise an die Möglichkeit des adaptiven Routings, die es Nachrichten erlaubt, unterschiedliche Pfade zu benutzen). Ist eine Erhaltung der Reihenfolge gewünscht, kann dies explizit auf einer höheren Ebene ebenso erzwungen werden wie ein vollständig synchroner Versand, da beides nur einen Spezialfall der asynchronen Kommunikation darstellt.

2.1.2 become

Über die become-Operation legt ein Aktor sein Folgeverhalten (replacement behaviour) fest, das bestimmt, wie er auf die nächste Nachricht in der Mailqueue reagieren wird. Es muß ein neues Verhalten angegeben werden, das aus einer behaviour description besteht, die den abzuarbeitenden Programmcode enthält und parametrisiert sein kann. Diese Parameter sind Adressen anderer Objekte und werden acquaintances genannt. Der Programmcode enthält dann z.B. Anweisungen, um an solche acquaintances Nachrichten zu verschicken.

Zu beachten ist hierbei, daß lediglich das Verhalten für die Abarbeitung der nächsten Nachricht bestimmt wird, und das Verhalten, das die weitere Reaktion auf die aktuelle Nachricht definiert, nicht geändert wird. Dies ist ein wichtiger Aspekt zur Erhöhung der möglichen Parallelität und wird später in Zusammenhang mit actor machines noch einmal genauer angesprochen.

Bei anderen Objektmodellen besteht der innere Zustand aus den Werten objektlokaler Variablen. Ein Zustandsübergang geschieht dann durch Zuweisung neuer Werte an die Variablen. Da dies sequentiell geschieht, können inkonsistente Zwischenzustände entstehen. Außerdem gibt es Probleme beim gleichzeitigen Zugriff auf deise Variablen, wenn das Objekt zwei Nachrichten parallel abarbeiten soll. Durch die Verwendung eines monolithischen Verhaltens als innerer Zustand werden solche Probeme im Actor-Modell vermieden.

Ein rein funktionaler Aktor weist ein statisches Verhalten auf und ändert sein Verhalten nicht über eine become-Operation.

2.1.3 create

Mit Hilfe von create werden neue Aktoren erzeugt. Dazu muß ihr Verhalten, wie unter become beschrieben, spezifiziert werden. Dieses Verhalten legt fest, wie das Objekt auf den Empfang der ersten Nachricht reagieren wird. Ist in der behaviour description eine become-Anweisung enthalten, so kann die Reaktion auf nachfolgende Nachrichten entsprechend variieren.

Das aufrufende Objekt erhält als Ergebnis von create die identifizierende Mail-Adresse des neuen Aktors, die dann in weiteren Operationen sofort benutzt werden kann (beispielsweise als Ziel einer Nachricht).

2.2 nähere Betrachtung

Die Vorgänge, die in einem System von Aktoren ablaufen, kann man zur Verdeutlichung auch graphisch darstellen. Im Folgenden werden die oben aufgeführten Operationen anhand zweier solcher Darstellungen näher erläutert.

2.2.1 Aktoren und actor machines

ist eine abstrakte Darstellung eines Aktors. Dieser besteht aus einer beliebig großen Mail-Queue, deren Slots mit natürlichen Zahlen numeriert werden. Der Aktor X hat seine ersten n-1 Nachrichten schon abgearbeitet und bearbeitet momentan die Nachricht n. Die Operationen werden von einer sogenannten actor machine Xn ausgeführt. Eine solche actor machine besitzt einen inneren Zustand in Form eines Verhaltens, das die auszuführenden Primitiven bestimmt. Im Beispiel versendet Xn eine Nachricht an ein nicht eingezeichnetes Objekt, erzeugt einen neuen Aktor Y und legt damit gleichzeitig dessen initiale actor machine Y1 fest. Weiterhin führt die become-Operation zum Festlegen des replacement behaviour und damit zur Erzeugung der actor machine Xn+1.


Abbildung : abstrakte Darstellung eines Aktors


Wichtig ist, daß sehr viele dieser Vorgänge parallel ablaufen können. Und zwar kann man drei verschiedene Arten der Parallelität unterschieden:

Parallelität zwischen Sender und Empfänger:
Aufgrund der asynchronen Kommunikation kann der Sender einer Nachricht sofort weitere Operationen ausführen, auch wenn der Empfänger die Nachricht noch nicht vollständig bearbeitet hat oder sogar noch mit anderen Nachrichten beschäftigt ist.

Parallelität der einzelnen Operationen innerhalb einer actor machine:
Da die Operationen, die eine actor machine ausführt, im allgemeinen unabhängig voneinander sind (Ausnahme: die Mail-Adresse, die eine create-Operation liefert, muß bekannt sein, bevor diese in weiteren Primitiven verwendet werden kann), können sie gleichzeitig ausgeführt werden. In wären dies das Verschicken einer Nachricht, das Erzeugen von Y und die Bestimmung des Folgeverhaltens.

Parallelität der einzelnen actor machines:
Mehrere actor machines des gleichen Objekts können parallel arbeiten. Dies ist möglich, da sie eigene, unabhängige innere Zustände besitzen. D.h. im Beispiel kann Xn+1 arbeiten, sobald Xn den Folgezustand festgelegt hat und die Kommunikation n+1 eingetroffen ist (diese Nachricht kann natürlich schon längere Zeit in der Mail-Queue stehen), auch wenn Xn noch beschäftigt ist.

2.2.2 event diagrams und garbage collection

Die wechselseitigen Abhängigkeiten und das dynamische Erzeugen neuer Aktoren können gut in einem event diagram sichtbar gemacht werden (). Diese Diagramme bestehen aus senkrechten Linien, den lifelines (Lebenslinien), die jeweils für ein bestimmtes Objekt stehen. Auf diesen Linien wird für jedes event (das Eintreffen einer Nachricht) ein Knoten eingezeichnet. Das Versenden von Nachrichten wird mit Pfeilen verdeutlicht, die von einem Knoten zu einem anderen führen und beim Ziel-Objekt wieder ein event auslösen. Weiterhin bedeuten gestrichelte Pfeile create-Operationen, die neue Aktoren und somit lifelines erzeugen. Die Halbbögen am Anfang einer Lebenslinie bedeuten die >>Geburt<< dieses Objekts, während fehlende Bögen darauf hindeuten, daß das zugehörige Objekt schon länger existiert.


Abbildung : event diagram


Diese Diagramme drücken dabei nicht direkt zeitliche Zusammenhänge aus, wie man meinen könnte, da es in einem verteilten System aufgrund der Laufzeiten der Nachrichten keine absolute, globale Zeit geben kann (im Idealfall, also bei minimal möglichen Laufzeiten, stößt man hier auf die spezielle Relativitätstheorie). Anstelle einer solchen globalen Ordnung tritt hier einerseits eine lokale Ordnung der events jedes Aktors und andererseits eine Halbordnung Zwischen events unterschiedlicher Aktoren, die durch den Austausch von Nachrichten bestimmt wird.

Auffallend ist noch, daß die Lebenslinien kein definiertes Ende haben. Dies kommt daher, daß es keine explizite Möglichkeit zum Zerstören bestehender Aktoren gibt. Da das Löschen von nicht erreichbaren Objekten auf konzeptioneller Ebene keinerlei Bedeutung hat (bei realen Implementierungen aber durchaus Auswirkungen auf die Performance des Systems hat), wird dies der jeweiligen Implementierung überlassen. Dort geschieht dies mit dem üblichen Verfahren der garbage collection. Dabei wird ausgenutzt, daß ein Objekt, dessen Adresse keinem anderen Objekt mehr bekannt ist und das keine zur Zeit aktiven actor machines besitzt, keine Nachrichten mehr empfangen und somit auch keine Aktionen mehr ausführen kann. Daher kann es in einem solchen Fall aus dem System entfernt werden.

Beispiel

Als Beispiel eines einfachen Programms, das auf Aktoren basiert, sehen wir uns die klassische rekursive Berechnung der Fakultäts-Funktion an (s. auch [1] und [4]). Zur Vereinfachung wurde das Programm in einem Pseudocode implementiert.

Das komplette System besteht aus 3 Verhaltensbeschreibungen: der eigentlichen Fakultätsberechnung (Fakultät), einem Hilfsobjekt (Customer) und dem aufrufenden Client. Dieser Client erzeugt ein neues Fakultäts-Objekt und sendet diesem anschließend die Nachricht, 3! zu berechnen und das Ergebnis an stdout zu schicken (das Objekt stdout soll hier einfach sämtliche Nachrichten auf den Bildschirm ausgeben).



Fakultät with acquaintances self let communication be an integer n and an actor u become Fakultät(self) if n=0 then send [1] to u else let c=create Customer with acq. n and u {send [n-1, c] to self} Customer with acq. an integer n and an actor u let communication be an integer k {send [n*k] to u } Client with acquaintances stdout let f=create Fakultät with acquaintances f {send [3,stdout] to f}

Listing : rekursive Fakultätsberechnung


Der erzeugte Fakultäts-Aktor erhält also eine Nachricht mit n=3 dund u=stdout. Da es sich um einen rein funktionalen Aktor handelt, wird mit become ein identisches Verhalten festgelegt. Wenn nun n=0 ist (Basis der Rekursion), so lautet das Ergebnis 1 und wird direkt an das gewünschte Ziel u gesendet. Ansonsten würde in einer herkömmlichen Sprache einfach n*fak(n-1) berechnet. Die Bestimmung von fak(n-1) geschieht einfach durch Senden einer entsprechenden Nachricht an sich selbst. Ein Problem ist aber die anschließende Multiplikation und Senden des Ergebnisses an u (stdout).

Zu diesem Zweck dient ein sogenannter customer, der hier als Parameter bei seiner Erzeugung einen Integer n und ein Objekt u erhält. Dieser Aktor macht nun nichts anderes, als eine ihm übermittelte ganze Zahl mit n zu multiplizieren und das Ergebnis an u weiterzuleiten.

Die Fakultäts-Berechnung erzeugt nun einen solchen customer und schickt sich anschließend die Nachricht, fak(n-1) zu errechnen und das Ergebnis an eben diesen customer zu senden, der dann seinerseits dafür sorgt, daß fak(n-1) mit n multipliziert wird und an das ursprüngliche Ziel stdout geschickt wird. Zur Berechnung von fak(n-1) kann es natürlich wieder notwendig sein, einen weiteren customer zu erzeugen und fak((n-1)-1) zu errechnen usw.

In ist der Ablauf der Berechnung von 3! noch einmal als event diagram verdeutlicht. Man sieht, wie drei Customer-Objekte erzeugt werden, die für die Multiplikationen sorgen und ihre Ergebnisse jeweils an den customer senden, der einen Schritt vorher erzeugt wurde. Der zuerst erzeugte customer kommuniziert schließlich mit dem ursprünglichen Ziel stdout.

Die Rekursivität wird an den drei Nachrichten deutlich, die der Fakultäts-Aktor sich selbst schickt. Diese werden im Bild durch die drei Pfeile dargestellt, die von der Fakultäts-Lebenslinie zu ihr zurückweisen.


Abbildung : rekursive Fakultätsberechnung


2.3 Zusammenfassung

Das Actor-Modell stellt eine einfache, aber leistungsfähige Grundlage dar, auf der parallele, verteilte Systeme auf objektorientierte Weise aufgebaut werden können. Dabei wird ein höchstmöglicher Grad von Parallelität erreicht, ohne daß in Programmen explizit gemacht werden muß, an welchen Stellen ein paralleler Ablauf gewünscht ist. Dies gelingt durch die weitgehende Unabhängigkeit der einzelnen Aktoren bzw. actor machines (Informationsaustausch nur über Versenden von Nachrichten, keine gemeinsam benutzten Variablen zwischen den actor machines) und den asynchronen Nachrichtenaustausch.

Andererseits bieten die zur Verfügung stehenden primitiven Operationen nur eine Art Maschinensprache, auf der programmiersprachliche Abstraktionen aufbauen müssen (z.B. zum automatischen Erzeugen von customers), will man größere Systeme modellieren.


3 Das ActorSpace-Modell

Das ActorSpace-Modell basiert auf dem Actor-Modell und erweitert dies um einige neue Operationen. Hierzu gehören eine Möglichkeit, Aktoren in logischen Gruppen zusammenzufassen, ganze Gruppen von Objekten als Ziel einer Nachricht zu verwenden und allgemein eine abstraktere Art der Adressierung als die direkte Angabe einer Mailadresse.

Um dies zu realisieren werden einige neue Konzepte eingeführt: attributes, die Eigenschaften festlegen, die zur Auswahl eines Objekt benutzt werden können (über pattern-matching), actorspaces, die als Behälter für Aktoren dienen und schließlich capabilities, Zugriffsschlüssel, die es nur ihren Besitzern erlauben, bestimmte Aktionen auszuführen.

3.1 Kommunikation im ActorSpace

Wie schon kurz angesprochen, sind die Kommunikationsmöglichkeiten im ActorSpace-Modell vielfältiger als im Actor-Modell. Und zwar gibt es nun zwei verschiedene Kommunikations-Primitiven: zum einen das schon bekannte send to, das dazu dient, eine Nachricht an genau ein Objekt zu verschicken; zum anderen broadcast, mit dessen Hilfe es möglich ist, eine identische Nachricht an eine ganze Gruppe von Aktoren zu senden.

In direktem Zusammenhang mit der neuen Kommunikations-Primitive stehen zwei unterschiedliche Varianten, ein Ziel zu adressieren. Bei der herkömmlichen Adressierung über die eindeutige Mailadresse eines Objektes verhalten sich send to und broadcast identisch: Beide verschicken die angegebene Nachricht einfach an den spezifizierten Aktor. Die alternative Adressierung geschieht unter Angabe eines actorspace und eines Musters von Attributen. Es werden alle im angegebenen actorspace enthaltenen Aktoren mit dem Suchmuster verglichen (pattern-matching). Trifft das Muster zu, so wird das Objekt zu einem potentiellen Empfänger der Nachricht. Gibt es mehrere solcher Aktoren, so unterscheiden sich die beiden Kommunikationsoperationen: Wird send to verwendet, so wählt das System genau ein Objekt aus der Menge der potentiellen Adressaten aus und schickt diesem die Nachricht. Die genaue Vorgehensweise bei der Auswahl bleibt dem System überlassen und kann im allgemeinen nicht vorhergesagt werden (eine Implementierung könnte hier Effizienzaspekte berücksichtigen). Bei broadcast hingegen werden alle zutreffenden Objekte zu Empfängern derselben Nachricht.

Tritt der Fall ein, daß kein Objekt im gewünschten actorspace das Suchmuster erfüllt, so gibt es mehrere Möglichkeiten, darauf zu reagieren:

Die Nachricht wird einfach nicht verschickt, da kein passender Adressat vorhanden ist.

Die Nachricht wird gespeichert und an das erste Objekt gesendet, daß später im actorspace sichtbar wird und zu dem Muster paßt. D.h. die Nachricht wird an ein oder kein Ziel verschickt.

Bei broadcast kann die Nachricht auch persistent sein, d.h. sie wird an alle Aktoren geschickt, die zukünftig im angegebenen actorspace die Kriterien erfüllen. Allerdings erhält jedes Objekt die Nachricht höchstens einmal.

Bei einer Implementierung des Modells steht es einem frei, sich für eine der drei Varianten zu entscheiden. Bei der in [3] beschriebenen Implementierung wurde die zweite Variante gewählt.

Wie schon im Actor-Modell garantiert das Mail-System eine Auslieferung der Nachricht. Die Reihenfolge hingegen, in der die Nachrichten empfangen werden, kann nicht vorhergesagt werden. So kann es z.B. vorkommen, daß broadcast-Nachrichten bei zwei Aktoren in unterschiedlicher Reihenfolge in deren Mailqueues stehen. Wenn solche Nachrichten einer bestimmten Ordnung folgen sollen, so kann dies unter Benutzung spezieller Aktoren geschehen, die die Nachrichten mittels eines eigens dafür definierten Protokolls an die Ziele schicken und dabei den Erhalt der Reihenfolge garantieren. Im allgemeinen ist es aber effizienter, auf solch ein Verfahren zu verzichten, da es zusätzlichen Overhead bedeutet und die mögliche Parallelität einschränken kann.

3.2 erweiterte Operationen

Das ActorSpace-Modell führt zusätzlich zu den bekannten actor primitives einige neue Operationen ein, um actorspaces und Schlüssel zu erzeugen bzw. zu verwalten. Auf die beiden Operationen send_to und broadcast wurde weiter oben schon ausführlich eingegangen. Aus diesem Grund werden sie hier nicht noch einmal aufgeführt.

3.2.1 make_capability

Mit dieser Operation werden neue, eindeutige Schlüssel erzeugt, eben sogenannte capabilities. Diese Schlüssel sind unteilbare Werte, die nicht manipuliert und nur durch Aufruf dieser Operation erzeugt werden können. Sie können aber in Nachrichten enthalten sein und somit auch anderen Aktoren als dem Aufrufer von make_capability bekannt gemacht werden.

Capabilities werden benötigt, um nur einigen ausgewählten Objekten bestimmte Operationen zu wie z.B. make_visible zu erlauben (s.u.). Dabei kann derselbe Schlüssel durchaus für mehrere verschiedene Aktoren benutzt werden.

3.2.2 new_actor

New_actor ersetzt create aus dem Actor-Modell. Es dient ebenfalls zum Erzeugen neuer Aktoren und benötigt als Parameter die Angabe einer behaviour description. Neu ist die optionale Angabe einer capability, über die dann das Objekt vom Besitzer des Schlüssels in einem actorspace sichtbar oder unsichtbar gemacht werden kann. Wird kein solcher Schlüssel angegeben, so kann nur das Objekt selbst diese Operationen durchführen.

Zu beachten ist, daß der neu erzeugte Aktor noch in keinen actorspace enthalten ist und somit nicht über pattern matching adressiert werden kann. Um dies zu ermöglichen, muß das Objekt später explizit mit make_visible in einen actorspace aufgenommen werden.

New_actor liefert dem Aufrufer die eindeutige Mailadresse des neuen Objekts zurück, die in weiteren Operationen verwendet werden kann.

3.2.3 new_space

New_space dient zum Erzeugen eines actorspace. Der einzige Parameter ist eine capability. Actorspaces können neben Aktoren auch weitere actorspaces enthalten, so daß sich damit unter anderem hierarchische Strukturen aufbauen lassen. Der Besitz des zu einem actorspace gehörenden Schlüssels erlaubt es dem Besitzer, diesen in anderen actorspaces sichtbar oder unsichtbar zu machen oder ihn wieder zu entfernen.

Da ein actorspace ein passives Gebilde ist, kann dieser solche Aktionen nicht selbständig durchführen. Statt dessen muß dies von einem Aktor übernommen werden. Die Angabe der capability ist deshalb nicht optional.

3.2.4 make_visible, make_invisible

Mit Hilfe dieser zentralen Operation wird ein Aktor in einem actorspace sichtbar gemacht, so daß er über die weiter oben angesprochenen Adressierungsmöglichkeiten (pattern matching) als Empfänger ausgewählt werden kann. Es müssen vier Parameter angegeben werden: der Aktor, der actorspace in den er aufgenommen werden soll, die Attribute, unter denen er in diesem actorspace sichtbar sein soll und die zum Aktor gehörende capability. Die Übergabe des Schlüssels stellt sicher, daß diese Operation nur vom Objekt selbst (dann entfällt die Angabe des Schlüssels) und vom Besitzer der Schlüssels aufrufbar ist.

Über den genauen Aufbau der Attribute sowie der Suchmuster wird im Modell nichts ausgesagt. Beispielsweise wäre eine Repräsentierung in der Form von Zeichenketten denkbar, wobei die Suchmuster dann aus bekannten regular expressions aufgebaut sein könnten.

Die inverse Operation make_invisible entfernt entfernt die Sichtbarkeit eines Aktors wieder unter Angabe seiner Mailadresse, des actorspace und der capability des Aktors. Außerdem können beide Operationen ebenso auf actorspaces angewandt werden.

3.3 garbage collection

Die Möglichkeit, Aktoren über Suchmuster in actorspaces anzusprechen, hat Auswirkungen auf die Lebenszeit der Objekte. Solche Aktoren, die in einem actorspace sichtbar sind, bleiben potentielle Empfänger, auch wenn ihre Mailadresse keinem anderen Objekt mehr bekannt ist (unter der Voraussetzung, daß der zugehörige actorspace einem anderen Objekt direkt oder indirekt, also durch Sichtbarkeit in einem anderen bekannten actorspace, bekannt ist). Daher können solche Objekte erst gelöscht werden, wenn sie mittels make_invisible unsichtbar gemacht wurden.

Das Objekt (oder die Objekte), die eine einem actorspace zugehörige capability besitzen, haben zusätzlich die Möglichkeit, den actorspace mit delete_space zu löschen. Die garbage collection hat dann die Möglichkeit, nun unsichtbar gewordene Objekte aus dem System zu entfernen. Das Löschen von actor_spaces kann aber ebenfalls analog zu Aktoren bei Nichtansprechbarkeit automatisch geschehen.

3.4 Beispiel

Zur Verdeutlichung betrachten wir eine sehr einfache Version einer verteilten Druckerverwaltung. In einem Netzwerk gebe es eine große Anzahl von Druckern, die sich in der verwendeten Beschreibungssprache unterscheiden (Postscript, ASCII, ...). Jedem Drucker ist ein Spool-Aktor zugeordnet, der die Druckjobs zwischenspeichert und zum Drucker schickt.

Eine Anwendung, die einen Job ausdrucken möchte, schickt diesen nicht direkt an den Spool-Aktor, sondern an einen darauf aufsetzenden PrintManager. Dieser ist ein Aktor, der dafür sorgt, daß der Spool-Aktor, den er verwaltet, nicht überlastet wird.

Die PrintManager werden dabei in einer einfachen Hierarchie von Actorspaces organisiert. Die Wurzel bildet der ActorSpace PrintManager, der Postscript, ASCII, etc. Als Unterräume enthält. In diesen Unterräumen schließlich sind die PrintManager-Aktoren selbst sichtbar.

Um nun einen Druckauftrag zu starten, wird einfach eine Nachricht an alle Objekte im gewünschten ActorSpace geschickt, also in etwa send-to '/PrintManager/Postscript/*' (job)3. Vom Mailsystem wird dann ein passender Manager ausgewählt, der die Nachricht erhält.

Der PrintManager selbst (s. ) ist sehr einfach gehalten. Er wurde mit zwei acquaintances erzeugt: dem zu verwaltenden Spooler und der Anzahl der anstehenden Druckjobs (bei Erzeugung Null). Bei Erhalt einer new_job-Nachricht überprüft er, ob weniger als 10 Druckjobs anstehen. Ist dies der Fall, so wird der Job an PrintSpool weitergeleitet und mittels become die Anzahl der Aufträge erhöht. Wenn Überlastung droht ([[congruent]]10 Aufträge), so wird die Nachricht einfach an irgendeinen anderen Manager derselben Gruppe geschickt. Außerdem entfernt sich der Aktor selbst aus seinem4 ActorSpace, um zu vermeiden, daß weitere Aufträge überhaupt erst zu ihm gelangen.

Wurde ein Auftrag erfolgreich ausgeführt, so sendet PrintSpool eine job_done-Nachricht an den Manager. Dieser dekrementiert dann einfach die Anzahl der noch ausstehenden Druckjobs und macht sich ab einer gewissen Schwelle sichtbar, um wieder Aufträge erhalten zu können.


behaviour Printmanager(PrintSpool, numJobs)
{
  method new_job(printjob)	// NEUER AUFTRAG
  {
    if (numJobs<10)		// NOCH NICHT UEBERLASTET
    {
      send-to PrintSpool new_job(printjob)
      become Printmanager(PrintSpool, numJobs+1)
    }
    else 		// UEBERLASTUNG DROHT -> SCHICKE JOB WEITER
    {
      send-to "./*" new_job(printjob)
      make_invisible self "."
    }
  }

  method job_done	// EIN AUFTRAG ERFOLGREICH BEENDET
  {
    become PrintManager(PrintSpool, numJobs-1)
    if (numJobs=5) make_visible self "."
  }
}
Listing : PrintManager

Bei diesem Ansatz können z.B. weitere Drucker dem System leicht hinzugefügt werden, indem für sie jeweils ein Spool-Aktor und ein PrintManager erzeugt werden und dieser dann im entsprechenden ActorSpace sichtbar gemacht wird. Clients können diesen Drucker dann sofort nutzen, ohne daß sie über dessen Existenz informiert werden müssen.

3.5 Zusammenfassung

Das ActorSpace-Modell bietet Erweiterungen, die wichtig sind, um größere Systeme von Aktoren zu organisieren. Durch die Einführung der actorspaces ist es möglich, Aktoren logisch zu gruppieren und mittels einer abstrakteren Adressierung (pattern matching) anzusprechen. Dies ermöglicht es beispielsweise neu in das System gekommenen Aktoren (Clients), die Dienste anderer Aktoren (Server) in Anspruch zu nehmen, deren genaue Adresse sie nicht zu kennen brauchen. Damit dies funktioniert, müssen natürlich bestimmte Konventionen über die Struktur der actorspaces und der Attribute getroffen werden.

Weiterhin können mehrere gleichwertige Server auf verschiedenen Orten in einem Netzwerk vorhanden sein. Will nun ein Client einen Dienst eines solchen Servers nutzen, so kann das System automatisch den Server auswählen, der am wenigsten belastet oder dem Auftraggeber am nächsten ist. Die Verwendung von broadcast kann dann dazu genutzt werden, um die Robustheit zu erhöhen, indem allen Server-Aktoren die gleiche Nachricht geschickt wird und deren Ergebnisse dann auf Abweichungen untersucht werden können.


4 Resümee

Das Actor-Modell stellt eine äußerst einfache und leistungsfähige Grundlage dar, auf der parallele Systeme aufgebaut werden können. Die Erweiterungen des ActorSpace-Modells helfen bei der Erstellung größerer verteilter Systeme, indem weniger stark gekoppelte Beziehungen zwischen den Objekten ermöglicht werden.

Diese Leistungsfähigkeit bekommt man aber nicht umsonst: Anders als andere Konzepte, die beispielsweise bekannte sequentielle Programmiersprachen nur um neue Möglichkeiten erweitern, erzwingt das ActorSpace-Modell ein neues Programmier-Paradigma, das ein gewisses Umdenken erfordert. Hier kann man natürlich argumentieren, daß die herkömmlichen Programmiermodelle für parallele Abläufe zumindest zum Teil einfach ungeeignet sind, und deshalb ein neues Modell erforderlich ist.

Die Abläufe in diesem Modell bestehen zum Großteil aus dem Versand von Nachrichten. Dadurch hat die Implementierung des Mail-Systems sehr große Auswirkungen auf die Performance einer Anwendung. Ob eine reine Software-Lösung auf bestehenden Hardware-Architekturen eine ausreichende Effizienz bereitstellen kann, oder ob neue, die grundlegenden actor primitves unterstützende Prozessoren sinnvoll sind, müssen Implementierungen des Modells zeigen.


5 Literaturverzeichnis

Gul Agha. Actors: A Model of Concurrent Computation in Distributed Systems. The MIT Press, Cambridge, Massachusetts, 1986.

Gul Agha. Concurrent Object-Oriented Programming. Communications of the ACM, 33(9):125-141, 1990.

Christian J. Callsen, Gul Agha. Open Heterogenous Computing in ActorSpace. Journal of Parallel and Distributed Computing, 21:289-300, 1994.

C.E. Hewitt. Viewing control structures as patterns of passing messages. Journal of Artificial Intelligence, 8-3:323-364, June 1977.


Frank Thilo - 12.12.95