Scala, and some other languages like Erlang, use the well known (and very old) high-level Actor model of concurrency.
An actor is a concurrent process that communicate with other actors by exchanging messages. It is assumed that shared resources are not used, what greatly simplifies the use of the model. [Of course, we can create an actor to manage a "de facto" shared resource, but this resource technically belongs to the single actor.]
An actor is an reactive entity that reacts to the messages by making local decisions and send more messages. An actor can have, and often have, local state. An actor can communicate with any other actor it knows and the actors themselves can be passed in messages.
We can regard an actor as a kind of object because it is able to react to messages, and can have local state. However there is a difference: object-oriented software is typically executed sequentially, while the Actor model is inherently concurrent. This is a very important point: communications among actors is asynchronous.
In most Actor systems, the messages that an actor has received and have not been handled yet are buffered in a structure known as mailbox. An actor can select the next message to handle by using pattern-matching and the order of arrival of the messages is also important. Typically, an actor runs an infinite loop in which it deals with the messages, one by one. When there are no messages available, the actor blocks, waiting for more messages to come. Note that a single actor is not internally concurrent because the messages are always handled one by one.
The Actor Model it is superior to shared state parallel programming because it reduces many pitfalls like deadlocks and race conditions, and this can be observed in practice. However deadlocks are still possible because locking is required in the management of the mailboxes.
Note that using actors no one need to deal with explicit locking and thread management. It is easier to write correct concurrent systems.
Actors is not the only concurrency model available in Scala. For example, Scala can also supports the lower level threads of Java and the accompanying basic mechanisms of mutual exclusion of shared resources: wait, notify and synchronized, provided by class AnyRef.
In the history of Scala, three kinds of Actors systems have been introduced:
In this course we will use lightweight actors only.
In Scala, an actor is a object that is created by instantiating a subclass of the trait Actor, or by calling the actor method. Interestingly enough, the messages are also objects, usually defined with case object to enable pattern-matching over them.
The actors in Scala go beyond the basic model in that synchronous messages are also available. However this kind of messages should be used rarely and carefully.
Below, is a summary of the main Scala operations related with actors. They are defined in the trait Actor and in the singleton object Actor, inside the package scala.actors.
import scala.actors._ import scala.actors.Actor._ // The simpler way to define a new actor (sometimes called an in-line actor) actor { ... } // Implementation of in-line actors: def actor(body: => Unit): Actor = { val actor = new Actor { def act() = body } actor.start() actor } // The behavior of an actor is specified by implementing this abstract method. def act(): Unit = ... // Returns the currently executing actor. Use 'self' instead of 'this' inside the method act. def self: Actor = ... // Sends msg to an actor (asynchronous). def !(msg: Any) : Unit = ... // Sends msg to an actor and awaits reply (synchronous). def !?(msg: Any): Any = ... // The actor which sent the last received message. // OutputChannel is a trait that provides a common interface // for all channels to which values can be sent. The trait // Actor extends OutputChannel[Any] def sender: OutputChannel[Any] = ... // Reply to the sender of the current handled message. def reply(msg: Any): Unit = sender ! msg def reply(): Unit = sender ! () // Returns the number of messages in self's mailbox. def mailboxSize: Int // The actor kills himself. This is a private operation. def exit: Unit // Causes self to repeatedly execute body. Compatible with the method react. def loop(body: => Unit): Unit = ... // Receives a certain kind of message from the mailbox of self. // Blocks self if that kind of message is not currently available. // This method never returns. def react(f: PartialFunction[Any, Unit]): Nothing // Receives a certain kind of message from the mailbox of self. // Blocks at most msec milliseconds if no message matching any of the // cases of f can be received. If no message could be received the // TIMEOUT action is executed if specified. // This method never returns. def reactWithin(msec: Long)(f: PartialFunction[Any, Unit]): NothingExample of set of messages:
case object Ping case object Pong case object StopExample of simple actor:
class Pong extends Actor { def act() { var pongCount = 0 loop { react { case Ping => if (pongCount % 1000 == 0) println("Pong: ping "+pongCount) sender ! Pong pongCount = pongCount + 1 case Stop => println("Pong: stop") exit } } } }
More detailed information about the actor operations: here.
Programming using only asynchronous messages turns out to be a very particular style of programming that worth a try.