scala concurrency actor

¿Pueden los actores de Scala procesar múltiples mensajes simultáneamente?



concurrency (3)

La respuesta a una pregunta reciente mía indicaba que un actor procesaba sus mensajes de a uno por vez . ¿Es esto cierto? No veo nada que diga explícitamente (en Programming in Scala ), que contiene el siguiente fragmento (p. 593)

Si [el método de react ] encuentra un mensaje que puede manejarse, [él] programará el manejo de ese mensaje para su posterior ejecución y emitirá una excepción

(Énfasis mío) Dos preguntas relacionadas (y mutuamente excluyentes):

  1. Asumiendo que un actor puede procesar múltiples mensajes simuladamente, ¿cómo puedo obligar a un actor a procesar mensajes 1 a la vez (si esto es lo que deseo hacer)? (¿usando receive ?)
  2. Suponiendo que un actor procese mensajes de a uno por vez, ¿cómo implementaría mejor un actor que, de hecho, podría procesar mensajes simultáneamente?

editar: haciendo un poco de prueba parece confirmar que estoy equivocado y que los actores son de hecho secuenciales. Así que es la pregunta n. ° 2 a la que necesito responder


Creo que la respuesta es que un Actor no puede manejar los mensajes de forma sincrónica. Si tiene un Actor que debería estar escuchando mensajes donde estos mensajes pueden manejarse de manera asíncrona , entonces podría escribirse así:

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


Los actores procesan un mensaje a la vez. El patrón clásico para procesar mensajes múltiples es tener un frente de actor coordinador para un grupo de actores consumidores. Si usa reaccionar, el grupo de consumidores puede ser grande pero solo usará una pequeña cantidad de subprocesos de JVM. Aquí hay un ejemplo en el que creo un grupo de 10 consumidores y un coordinador para ellos.

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)

Este código prueba para ver qué consumidor está disponible y envía una solicitud a ese consumidor. Las alternativas son simplemente asignar aleatoriamente a los consumidores o utilizar un programador round robin.

Dependiendo de lo que esté haciendo, es posible que esté mejor servido con Scala''s Futures. Por ejemplo, si realmente no necesita actores, entonces toda la maquinaria anterior podría escribirse como

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


Si quiere hacer varias cosas, entonces debe usar múltiples actores. La razón para usar actores es dividir el trabajo entre múltiples procesos independientes.