Nebenläufigkeit heute: Aktoren

Einleitung

Der Entwurf von nebenläufigen Anwendungen stellt seit Jahren eine Herausforderung dar, für die es keine einfache Lösung gibt. Mittlerweile wurden unterschiedliche Möglichkeiten entwickelt, diesem Problem zu begegnen. Das Aktoren-Modell stellt dabei eine einfache und erprobte Möglichkeit dar, nebenläufige Algorithmen zu implementieren.

Um die Nutzung des Aktoren-Modells zu verdeutlichen, stelle ich in einer Artikel-Serie die Nutzung am Beispiel des Frameworks Akka vor, ohne zu stark auf die Theorie dahinter einzugehen! Das Framework Akka implementiert das Aktoren-Modell für die Programmiersprachen Java und Scala und eignet sich sehr gut für die Nutzung in Cloud-Umgebungen.

Übersicht über die Artikel-Serie

Die Serie umfasst folgende Artikel:

  • Nebenläufigkeit heute: Aktoren (dieser Artikel)
  • Aufbau eines Aktoren-Modells mit Akka (noch nicht veröffentlicht)
  • Wiederverwendung und Identifikation: Aktor-Instanzen (noch nicht veröffentlicht)
  • Nachrichten-Persistenz mit Akka (noch nicht veröffentlicht)
  • Verteilte Systeme mit Akka (noch nicht veröffentlicht)
[Update 1: Warum sich mit Aktoren beschäftigen? Eine Begründung gibt es hier!]
[Update 2: Eine sehr kurze Übersicht habe ich auf der Informatik 2012 zum Thema Elastische Skalierbarkeit von Web-Anwendungen gegeben.]

Das Aktoren-Modell

Um das Aktoren-Modell besser zu verstehen, gibt es erst einen Überblick über das wesentliche Konzept. Daran anschließend erfolgt die Anwendung auf eine konkrete Fragestellung.

Übersicht

Bei nebenläufiger Programmierung stellen sich folgende Herausforderungen:

  • Zugriff auf gemeinsame Ressourcen: Dies wird zumeist über Sperren (“Locks”) realisiert, um zu gewährleisten, dass zu einer Zeit nur ein Prozess auf eine gemeinsame Ressourcen zugreift.
  • Synchronisation: Hierbei werden meistens Sprachmittel (“synchronized”-Schlüsselwort, o.ä.) eingesetzt, um eine Ausführungsreihenfolge zu garantieren

Beide Konzepte begrenzen die mögliche Parallelität, da für solche Stellen explizit eine sequentielle Verarbeitung erzwungen wird.

Als weiteres Problem existieren Deadlocks. Dabei handelt es sich um einen Ringschluss, bei dem eine Komponente über eine Zwischenstufe auf sich selbst wartet. Für dieses Problem gibt es keine einfache Lösung, außer der Simulation und einem bedachten Algorithmus-Design.

Aktoren als Alternative

Es gibt jedoch eine Lösung für diese Problem: das Aktoren Modell. Dabei handelt es sich um ein Modell von Carl Hewitt, dass nebenläufige Interaktion explizit beschreibt und formalisiert und damit die Nachteile der klassischen Entwicklung ausgleicht. Die Mitspieler hierbei sind:

  • die Aktoren, sowie
  • Nachrichten

Die Idee ist nun, jeden eigenständig lauffähigen, nebenläufigen Teil des Problems als eigenständigen Aktor aufzufassen. Die Lösung des Gesamtproblems ergibt sich durch Kommunikation der einzelnen Aktoren untereinander. Die Kommunikation erfolgt gerichtet und explizit durch das Senden einer Nachricht an den betreffenden Aktor. Das ganze lässt sich bildlich so darstellen:

image

In obigem Bild schickt Aktor 1 eine Nachricht (“Nachricht A”) an Aktor 2. Dieser bearbeitet die Nachricht und schickt vielleicht eine weitere Nachricht (“Nachricht B”) weiter an den nächsten Aktor. Das besondere an diesem Vorgehen ist, dass ein Aktor immer nur eine Nachricht bearbeitet. Dadurch ergibt sich eine geordnete Bearbeitungsreihenfolge. Um sicherzustellen, dass Aktor 2 nicht viele Nachrichten auf einmal erhält und bearbeitet, werden diese durch eine Warteschlage aufgefangen, die durch den Aktor abgearbeitet wird.

Aktoren in der Praxis

Es gibt viele Implementierungen von Aktoren, gerade im Kontext hochparalleler und ausfallsicherer Systeme. Die Programmiersprache Erlang setzt Aktoren explizit ein, um Nebenläufigkeit zu realisieren.

Auswirkungen des Aktoren-Modells

Von zentraler Bedeutung ist, dass ein Aktor mit seiner Arbeit fertig ist, wenn er

  • entweder nichts mehr zu tun hat (=am Ende seiner Methode/Funktion/Klasse angekommen ist), oder
  • eine Nachricht an einen anderen Aktor geschickt hat und auf eine Antwort wartet

Ein Aktor wartet nicht auf die Antwort eines anderen Aktors, sondern verarbeitet einfach die nächste aus seiner Warteschlange! Der Grund dafür ist, dass das Aktoren-Modell Synchronisation vermeidet und ein Wartezustand ist eine Form der Synchronisation.

Aktoren live: Ein einfaches Beispiel

Das folgende, einfache Problem soll die Problemlösung mit Aktoren verdeutlichen:

  • Es gibt ein Produkt, das aus verschiedenen Bauteilen besteht
  • Jedes Bauteil hat einen eigenen Preis
  • der Produktpreis ergibt sich aus der Summe der Bauteilpreise zzgl. einer Montagegebühr

Um das Problem in Aktoren zu zerlegen, modelliert man als erstes die Abhängigkeiten der Komponenten untereinander. Dies sieht für das Problem wie folgt aus:

image

Dieses Modell lässt sich so darstellen, dass das Produkt bei seinen Bauteilen nach den individuellen Preisen “fragt” und, nachdem es alle Preise beisammen hat, den Gesamtpreis errechnet. Das zugehörige Modell kann so aussehen:

image

Hierbei sendet der Produkt-Aktor Nachrichten (“getPrice”) an alle Bauteile. Anschließend arbeitet er wieder seine Warteschlange ab, bis Antworten eintreffen. Jeder Bauteil-Aktor verarbeitet die Nachricht und sendet seine Antwort an den Produkt-Aktor zurück. Entgegen der Darstellung handelt es sich bei der Kommunikation nicht um synchrone Aufrufe, sondern um eine asynchrone Kommunikation.

Damit der Produkt-Aktor den Gesamtpreis berechnen kann, müssen folgende Bedingungen erfüllt sein:

  • Der Aktor muss wissen, wie viele Nachrichten verschickt wurden
  • Der Aktor muss wissen, wie viele Antworten zurückgekommen sind

Die erste Bedingung ist relativ einfach zu realisieren: es werden so viele Nachrichten verschickt, wie es Bauteile gibt. Die zweite Bedingung stellt eine weitere Eigenschaft eines Aktors dar: ein Aktor speichert und verwaltet seinen Zustand (siehe auch hier (englisch)). Sobald die letzte Nachricht eingetroffen ist, kann die Berechnung durchgeführt und abgeschlossen werden. Anschließend kann das Ergebnis zurückgegeben , gespeichert , oder anderweitig verarbeitet werden.

[Ein weiteres Beispiel für Aktoren findet sich bei Florian Hopf.]

Laufzeit und parallele Anfragen

Durch die asynchrone Kommunikation zwischen den Aktoren, kann zu Beginn einer Verarbeitung keine Aussage darüber getroffen werden, wann diese beendet sein wird. Der Grund hierfür ist, dass jeder Aktor seine eigene Warteschlange mit (potenziell) unterschiedlichem Füllstand besitzt. Eine weitere Schwierigkeit ergibt sich, wenn mehrere Preisberechnungen (quasi) simultan erfolgen sollen. In diesem Falle muss noch eine Statusverfolgung vorgesehen werden, um die einzelnen Anfragen und Antworten auseinander zu halten.

Zusammenfassung und Ausblick

Dieser einführender Beitrag hat das Aktoren-Modell vorgestellt und anhand einer Trockenübung konkretisiert. In den nächsten Beiträgen werden wir dieses Modell exemplarisch anhand von Akka, implementieren.

2 Antworten auf „Nebenläufigkeit heute: Aktoren“

Schreibe einen Kommentar