Dieser Seitenbereich bezieht sich auf die ursprügliche Scala Aktor Implementierung. Die ursprüngliche Scala Aktor-Implementierung gilt seit der Version 2.10.0 von Scala als deprecated. Der Compiler gibt mit der Version 2.10.0 noch keine entsprechende Warnung aus, was aber wahrscheinlich mit einer der nächsten Versionen erfolgt. In einem zukünftigen Major-Release ist damit zu rechnen, dass die ursprünglichen Scala-Aktoren aus der Scala-Distribution entfernt werden.
Die neue Standard Aktor Implementierung der Scala-Distribution ist das Akka-Framework, welches auch Bestandteil des Typesafe-Stacks ist.
Dieses Tutorial ist nur für den Interessant, der sich mit "Alt - Anwendungen" beschäftigen muss oder eine Scala Version vor 2.1.0 verwenden muss. Jeder der eine neue Anwendung entwickelt und Scala in der Version 2.1.0 oder höher verwenden kann, sei angeraten, direkt Akka als Framework für Nebenläufigkeit zu verwenden.
Die nebenläufige Programmierung wird in Scala durch ein Aktor-Modell unterstützt. Das Modell ist dabei nicht Kernbestandteil der Sprache, sondern ist in der Scala Aktor Bibliothek realisiert.
Einen eigenen Aktor definiert man, indem die entsprechende
Klasse das Trait scala.actors.Actor
und dessen Methode
act()
implementiert. Das folgende Beispiel
MySimpleActor
zeigt beispielhaft die
Definition eines einfachen Aktors.
import scala.actors._ class MySimpleActor extends Actor{ def act(){ for (i <- 0 to 10) println("Acting value: "+i) } }
Gestartet wird ein Aktor durch Aufruf der Methode start()
des Traits scala.actors.Actor
.
scala> val myActor = new MySimpleActor() myActor: MySimpleActor = MySimpleActor@11b99c4 acala> myActor.start Acting value: 0 Acting value: 1 Acting value: 2 Acting value: 3 Acting value: 4 Acting value: 5 Acting value: 6 Acting value: 7 Acting value: 8 Acting value: 9 Acting value: 10 res5: scala.actors.Actor = MySimleActor@11b99c4 scala>
Ruft man die Methode act()
des Aktors direkt
auf wird die Methode wie jede andere Methode ausgeführt.
Die Methode läft dann allerdings nicht nebenläufig
zum umgebenden Kontext ab.
Das vorherige Beispiel hatte nur einen Aktor. Die nebenläufige Arbeitsweise war im Beispiel nicht zu erkennen. Um die nebenläufige Arbeitsweise besser zu sehen, folgt nun ein Beispiel, in dem zwei Aktoren verwendet werden.
import scala.actors.Actor object TwoActors { def main(args: Array[String]) { val myActor1 = new MyActor1 val myActor2 = new MyActor2 myActor1.start() myActor2.start() } } class MyActor1 extends Actor{ def act(){ for (i <- 0 to 10) println("MyActor1: "+i) } } class MyActor2 extends Actor{ def act(){ for (i <- 0 to 10) println("MyActor2: "+i) } }
Eine mögliche Ausgabe des Programmes kann wie folgt aussehen:
MyActor1: 0 MyActor2: 0 MyActor2: 1 MyActor2: 2 MyActor2: 3 MyActor2: 4 MyActor2: 5 MyActor2: 6 MyActor1: 1 MyActor1: 2 MyActor1: 3 MyActor2: 7 MyActor2: 8 MyActor2: 9 MyActor1: 4 MyActor1: 5 MyActor2: 10 MyActor1: 6 MyActor1: 7 MyActor1: 8 MyActor1: 9 MyActor1: 10
Ein grundlegender Ansatz in der nebenläufigen Programmierung
mit Aktoren ist es, das die Kommunikation der beteiligten
Aktoren nicht mithilfe gemeinsam genutzter Variablen
erfolgt. Die Kommunikation erfolgt beim Aktor Ansatz mit
Hilfe von Nachrichten, die dem jeweiligen Actor über
die Methode !
übermittelt werden.
Um diese Art der Kommunikation zu ermöglichen, besitzt jeder Aktor eine Nachrichtenbox, in der die eingehenden Nachrichten gesammelt werden. Die Arbeit des Aktors wird durch den Eingang einer Nachricht nicht unterbrochen, sondern er kann seine Nachrichten zu einem geeigneten Zeitpunkt abrufen und darauf reagieren. Auch ist es möglich, dass ein Aktor gar nicht auf eingehende Nachrichten reagiert. Das Objekt, welches eine Nachricht an einen Aktor sendet, arbeitet direkt nach dem Versand weiter, ohne (direkt) auf eine Antwort des Aktors zu warten.
Ein Aktor kann eine Nachricht aus seiner Nachrichtenbox mithilfe
eines receive
Blockes bearbeiten. Befindet sich
keine Nachricht in der Nachrichtenbox, wartet der Aktor,
bis er eine Nachricht empfängt. Die eingehenden Nachrichten
können durch ein Pattern Matching geeignet verarbeitet werden.
Das nachfolgende Beispiel MySimpleMessageActor
zeigt einen Aktor, welcher in einer Endlosschleife eingehende
Nachrichten verarbeitet.
import scala.actors.Actor class MySimpleMessageActor extends Actor{ def act(){ loop{ receive { case i : Int => println("got an Int message: "+i) case d : Double => println("got a Double message: "+d) case s : String => println("got a String message: "+s) case _ => println("unknown message type") } } } }
Im Beispiel MySimpleMessageActor
wird entsprechend des Typs (Objekt - Typ) der Nachricht
eine Ausgabe auf der Systemausgabe gemacht. Unterschieden werden hier Nachrichten vom Typ
Die Anweisung loop
ist eine Kurzschreibweise für
while(true)
Das nachfolgende Beispielprogramm erzeugt einen Actor vom Typ MySimpleMessageActor
,
startet ihn und sendet vier unterschiedliche Nachrichten an den Actor.
object TestMySimpleMessageActor { def main(args: Array[String]) { val mySimpleMessageActor = new MySimpleMessageActor() mySimpleMessageActor.start() mySimpleMessageActor ! 12.34 mySimpleMessageActor ! 3 mySimpleMessageActor ! "a message" mySimpleMessageActor ! 123f } }
Als Ergebnis des Programmablaufs erfolgt folgende Ausgabe auf der Systemausgabe:
got a Double message: 12.34 got an Int message: 3 got a String message: a message unknown message type
Bei der Verwendung der Methode receive
ist der Aktor solange blockiert,
bis eine Nachricht eintrifft. Gibt es keinen weiteren Aktor / Thread, der diesem Aktor eine
Nachricht sendet, verschwendet dieser Aktor unnötig Ressourcen. Abhilfe schafft hier die Methode
receiveWithin(msec: Long)
von Actor
. Trifft innerhalb von msec
Millisekunden keine Nachricht ein, wird ein scala.actors.TIMEOUT
als Nachricht
übergeben, worauf der Aktor entsprechend reagieren kann.
Das nachfolgende Beispiel ist eine Erweiterung von MySimpleMessageActor
und
TestMySimpleMessageActor
, welches die Verwendung von receiveWithin
zeigen soll.
import scala.actors.Actor import scala.actors.TIMEOUT class MySimpleMessageActor2 extends Actor{ def act(){ loop{ receiveWithin(1000) { case i : Int => println("got an Int message: "+i) case d : Double => println("got a Double message: "+d) case s : String => println("got a String message: "+s) case TIMEOUT => println("Timeout") case _ => println("unknown message type") } } } } object TestMySimpleMessageActor2 { def main(args: Array[String]) { val mySimpleMessageActor2 = new MySimpleMessageActor2() mySimpleMessageActor2.start() mySimpleMessageActor2 ! 12.34 mySimpleMessageActor2 ! 3 mySimpleMessageActor2 ! "a message" mySimpleMessageActor2 ! 123f while (true) { mySimpleMessageActor2 ! "Message in a loop" Thread.sleep(2000) } } }
Eine mögliche Ausgabe des Programms ist:
got a Double message: 12.34 got an Int message: 3 got a String message: a message unknown message type got a String message: Message in a loop Timeout Timeout Timeout got a String message: Message in a loop Timeout Timeout Timeout got a String message: Message in a loop Timeout Timeout ...