views:

1327

answers:

3

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

A: 

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.

mamboking
This is an unhelpful answer. Let's say I have some actor which reads in events to be processed and send them to some other actor to be processed. Except that actually, multiple events can be processed simultaneously, even if the actual processing code is exactly the same (which I'd therefore write in a single place, i.e. in 1 actor). How do I let the actors framework "replicate" (or create a pool) of these (identical) actors such that I can make the best use of my programming resources?
oxbow_lakes
I mean "best use of my processor resources"
oxbow_lakes
My answer was unhelpful because your question wasn't properly framed. An actor is a process not a piece of code. Just like an object is an instance of a class. You didn't ask how do I create multiple actors to handle multiple messages. You wanted to know how you could get an actor to process multiple messages simultaneously. My answer was an attempt to clarify what I perceived as your misunderstanding of the Actor model.
mamboking
You didn't attempt to clarify anything. Had you posted the contents of your comment instead, you'd have got an upvote; not a downvote
oxbow_lakes
+2  A: 

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
        }
      }
    }
  }
oxbow_lakes
+15  A: 

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))
James Iry
Can you explain what Ready means?
Seun Osewa
Ready in this example is a case class which acts as a message to pass.The coordinator sends a Ready message to the consumer which basically means 'are you ready?' and the consumer responds with a ready message back which means 'yes'.The first consumer to respond is then passed the Request.
harmanjd