Step 12. Read lines from a file
32.2 Actors and message passing
An actor is a thread-like entity that has a mailbox for receiving messages.
To implement an actor, you subclassscala.actors.Actorand implement theactmethod. An example is shown inListing 32.1. This actor doesn’t do anything with its mailbox. It just prints a message five times and quits.
Section 32.2 Chapter 32 ã Actors and Concurrency 726
import scala.actors._
object SillyActor extends Actor { def act() {
for (i <- 1 to 5) { println("I'm acting!") Thread.sleep(1000) }
} }
Listing 32.1ãA simple actor.
You start an actor by invoking itsstartmethod, similar to the way you start a Java thread:
scala> SillyActor.start() I'm acting!
res4: scala.actors.Actor = SillyActor$@1945696 scala> I'm acting!
I'm acting!
I'm acting!
I'm acting!
Notice that the “I’m acting!” output is interleaved with the Scala shell’s out- put. This interleaving is due to theSillyActoractor running independently from the thread running the shell. Actors run independently from each other, too. For example, given this second actor:
import scala.actors._
object SeriousActor extends Actor { def act() {
for (i <- 1 to 5) {
println("To be or not to be.") Thread.sleep(1000)
} } }
Section 32.2 Chapter 32 ã Actors and Concurrency 727 You could run two actors at the same time, like this:
scala> SillyActor.start(); SeriousActor.start() res3: scala.actors.Actor = seriousActor$@1689405 scala> To be or not to be.
I'm acting!
To be or not to be.
I'm acting!
To be or not to be.
I'm acting!
To be or not to be.
I'm acting!
To be or not to be.
I'm acting!
You can also create an actor using a utility method named actorin object
scala.actors.Actor:
scala> import scala.actors.Actor._
scala> val seriousActor2 = actor { for (i <- 1 to 5)
println("That is the question.") Thread.sleep(1000)
}
scala> That is the question.
That is the question.
That is the question.
That is the question.
That is the question.
Thevaldefinition above creates an actor that executes the actions defined in the block following theactormethod. The actor starts immediately when it is defined. There is no need to call a separatestartmethod.
All well and good. You can create actors and they run independently.
How do they work together, though? How do they communicate without using shared memory and locks? Actors communicate by sending each other messages. You send a message by using the!method, like this:
scala> SillyActor ! "hi there"
Section 32.2 Chapter 32 ã Actors and Concurrency 728 Nothing happens in this case, becauseSillyActoris too busy acting to pro- cess its messages, and so the"hi there"message sits in its mailbox unread.
Listing 32.2shows a new, more sociable, actor that waits for a message in its mailbox and prints out whatever it receives. It receives a message by calling
receive, passing in a partial function.1
val echoActor = actor { while (true) {
receive { case msg =>
println("received message: "+ msg) }
} }
Listing 32.2ãAn actor that callsreceive.
When an actor sends a message, it does not block, and when an actor re- ceives a message, it is not interrupted. The sent message waits in the receiv- ing actor’s mailbox until the actor callsreceive. You can see this behavior illustrated here:
scala> echoActor ! "hi there"
received message: hi there scala> echoActor ! 15 scala> received message: 15
As discussed in Section 15.7, a partial function (an instance of trait
PartialFunction) is not a full function—i.e., it might not be defined over all input values. In addition to an applymethod that takes one argument, a partial function offers anisDefinedAtmethod, which also takes one ar- gument. TheisDefinedAtmethod will returntrueif the partial function can “handle” the passed value. Such values are safe to pass toapply. If you pass a value toapplyfor whichisDefinedAtwould return false, however,
applywill throw an exception.
1As described inSection 15.7, apartial functionliteral is expressed as a series ofmatch alternatives or “cases.” It looks like amatchexpression without thematchkeyword.
Section 32.3 Chapter 32 ã Actors and Concurrency 729 An actor will only process messages matching one of the cases in the par- tial function passed toreceive. For each message in the mailbox,receive will first invoke isDefinedAton the passed partial function to determine whether it has a case that will match and handle the message. Thereceive method will choose the first message in the mailbox for whichisDefinedAt
returns true, and pass that message to the partial function’sapplymethod.
The partial function’s applymethod will handle the message. For exam- ple,echoActor’sapplymethod will print"received message: "followed by the message object’s toStringresult. If the mailbox contains no mes- sage for whichisDefinedAtreturns true, the actor on whichreceivewas invoked will block until a matching message arrives.
For example, here is an actor that handles only messages of typeInt:
scala> val intActor = actor { receive {
case x: Int => // I only want Ints println("Got an Int: "+ x) }
}
intActor: scala.actors.Actor = scala.actors.Actor$$anon$1@34ba6b
If you send a Stringor Double, for example, theintActor will silently ignore the message:
scala> intActor ! "hello"
scala> intActor ! math.Pi
But if you pass anInt, you’ll get a response printed out:
scala> intActor ! 12 Got an Int: 12