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
...