28
votes

The reply to a recent question of mine indicated that an actor processed its messages one at a time. Is this true? I see nothing that explicitly says that (in Programming in Scala), which contains the following snippet (pp. 593)

If [the react method] finds a message that can be handled, [it] will schedule the handling of that message for later execution and throw an exception

(Emphasis my own). Two related (and mutually exclusive) questions:

  1. Assuming an actor could process multiple messages simulatenously, how can I force an actor to process messages 1 at a time (if this is what I wish to do)? (using receive?)
  2. Assuming an actor processes messages one at a time, how would I best implement an actor which in fact could process messages concurrently

edit: doing a bit of testing seems to bear out that I am wrong and that actors are indeed sequential. So it's question #2 which I need answering

4
I really don't see how this question justifies a downvote. If I can read an entire chapter on actors in Programming in Scala and not be sure of the answer, it's a completely valid question.oxbow_lakes
A single actor processes messages sequentially because the idea is that one can use an actor to mediate access to an (otherwise) shared resource, and no other synchronization on the shared resource will be required. If messages could be processed sequentially, you'd need to use locks inside your actors to ensure that synchronization errors couldn't occur.Ken Bloom

4 Answers

26
votes

Actors process one message at a time. The classic pattern to process multiple messages is to have one coordinator actor front for a pool of consumer actors. If you use react then the consumer pool can be large but will still only use a small number of JVM threads. Here's an example where I create a pool of 10 consumers and one coordinator to front for them.

import scala.actors.Actor
import scala.actors.Actor._

case class Request(sender : Actor, payload : String)
case class Ready(sender : Actor)
case class Result(result : String)
case object Stop

def consumer(n : Int) = actor {
  loop {
    react {
      case Ready(sender) => 
        sender ! Ready(self)
      case Request(sender, payload) =>
        println("request to consumer " + n + " with " + payload)
        // some silly computation so the process takes awhile
        val result = ((payload + payload + payload) map {case '0' => 'X'; case '1' => "-"; case c => c}).mkString
        sender ! Result(result)
        println("consumer " + n + " is done processing " + result )
      case Stop => exit
    }
  }
}

// a pool of 10 consumers
val consumers = for (n <- 0 to 10) yield consumer(n)

val coordinator = actor {
  loop {
     react {
        case msg @ Request(sender, payload) =>
           consumers foreach {_ ! Ready(self)}
           react {
              // send the request to the first available consumer
              case Ready(consumer) => consumer ! msg
           }
         case Stop => 
           consumers foreach {_ ! Stop} 
           exit
     }
  }
}

// a little test loop - note that it's not doing anything with the results or telling the coordinator to stop
for (i <- 0 to 1000) coordinator ! Request(self, i.toString)

This code tests to see which consumer is available and sends a request to that consumer. Alternatives are to just randomly assign to consumers or to use a round robin scheduler.

Depending on what you are doing, you might be better served with Scala's Futures. For instance, if you don't really need actors then all of the above machinery could be written as

import scala.actors.Futures._

def transform(payload : String) = {      
  val result = ((payload + payload + payload) map {case '0' => 'X'; case '1' => "-"; case c => c}).mkString
  println("transformed " + payload + " to " + result )
  result
}

val results = for (i <- 0 to 1000) yield future(transform(i.toString))
6
votes

I think that the answer is that an Actor cannot handle messages aynchronously. If you have an Actor which should be listening to messages where these messages can be handled asynchronously, then it could be written like this:

val actor_ = actor {

  loop {
    react {
      case msg =>
        //create a new actor to execute the work. The framework can then 
        //manage the resources effectively
        actor {
          //do work here
        }
      }
    }
  }
0
votes

If you want to do multiple things then you should be using multiple actors. The whole reason to use actors is to divide up the work among multiple independent processes.

0
votes

You can try using the Futures concepts. Please store all these messages using Futures and then try processing those all.