2010-11-28 21 views
5

en Scala 2.8 cuando empiezo con actores, puedo comunicarme mediante el envío de mensajes. Esto a su vez significa que puedo enviar el último mensaje de Salida() o lo que sea que decida se ajusta a mi protocolo.Scala, compruebe si Actor ha salido de

Pero, ¿cómo voy a comprobar si un actor ha salido? Puedo imaginarme fácilmente a mí mismo teniendo una tarea en la que un actor principal inicia algunos actores trabajadores y luego simplemente espera las respuestas, cada vez que comprueba si esta fue la respuesta final (es decir, ¿algún actor sigue trabajando o todos salieron?).

Por supuesto, puedo permitirles a todos enviar de vuelta un mensaje "He terminado", y luego contarlos, pero esto es de alguna manera insatisfactorio.

¿Cuál es la mejor práctica cuando se evalúa la finalización de trabajadores-actores?

editar # 1

Hola chicos, Estoy buscando en Futuros, pero tiene problemas. ¿Puede alguien explicar por qué este código no funciona:

package test 
import scala.actors.Futures._ 

object FibFut extends Application{ 

    def fib(i:Int):Int = 
     if(i<2) 
      1 
     else 
      fib(i-1)+fib(i-2) 

    val f = future{ fib(3) } 

    println(f())  

} 

Funciona si defino la fib función dentro de la futura-cuerpo. Debe ser un alcance, pero no consigo ningún error con lo anterior, simplemente se bloquea. ¿Nadie?

editar # 2

Parece que la ampliación de la aplicación no era una buena manera de ir. Definir un método principal hizo que todo funcionara. El código siguiente es lo que estaba buscando, así Futuros obtener el visto bueno :)

package test 

import scala.actors.Futures._ 

object FibFut { 

    def fib(i: Int): Int = if (i < 2) 1 else fib(i - 1) + fib(i - 2) 

    def main(args: Array[String]) { 

    val fibs = for (i <- 0 to 50) yield future { fib(i) } 

    for (future <- fibs) println(future()) 

    } 

} 
+3

Uhm, este hilo fue interesante para mí: http://stackoverflow.com/questions/2721337/best-method-to-peek-into-a-scala-actors-mailbox, junto con el uso de este truco en una lista de actores: computers.map (_. getState == Actor.State.Terminated) .reduceRight (_ && _), para verificar si todos los actores dentro de la lista de computadoras han terminado. Si ese es el caso, el hilo maestro puede entrar en un estado de "terminar la lectura del buzón y salir" con el uso si reacciona dentro. Voy a publicar la solución si lo hago :-) – Felix

Respuesta

3

Soy un fanático de los mensajes "He terminado", personalmente; es una buena forma de administrar la distribución del trabajo, y como beneficio adicional, ya sabes cuándo todos los niños han terminado lo que están haciendo.

Pero si realmente quiere terminar alguna tarea una vez y espere hasta que todo esté listo, consulte scala.actors.Futures.Se le puede pedir que hacer algún cálculo:

val futureA = Futures.future { 
    val a = veryExpensiveOperation 
    (a,"I'm from the future!") 
} 

y entonces se puede esperar a que todo para completar, si se han realizado múltiples solicitudes:

Futures.awaitAll(600*1000, futureA, futureB, futureC, futureD) 
// Returns once all of A-D have been computed 
val actualA = futureA() // Now we get the value 
+0

Ocupado esperando en el maestro? :( –

+0

De acuerdo, los futuros se ven muy interesantes. Veo esta función en la clase Future: def isSet: Boolean, que es probablemente lo que necesito. ¿Hay algún tutorial/ejemplo sobre cómo crear/administrar sus propios objetos futuros sin usando el Futures-companion-object? – Felix

+0

@Viktor - En el modelo "I'm done", el maestro reacciona a los mensajes "I'm done" de los trabajadores. En el modelo "hazlo una vez", el maestro es solo una forma práctica de encapsular futuros múltiples en un orden razonable. No veo dónde estaría la agitación. –

2

Hace un tiempo escribí a post en la vinculación de los actores en Scala. La vinculación de actores es una forma idiomática [y la más fácil] de monitorear actores en Erlang, Scala Actors y otras bibliotecas de actores. Por defalt, cuando se vincula 2 actores, y uno de ellos muere, otro muere inmediatamente también (a menos que las trampas de actor/Palancas señal de salida):

scala> case object Stop 
defined module Stop 

scala> 

scala> val actor1 = actor { 
    | loop { 
    |  react { 
    |   case Stop => 
    |    println("Actor 1: stop") 
    |    exit() 
    |   case msg => println(msg) 
    |    } 
    |   } 
    | } 
actor1: scala.actors.Actor = [email protected] 

scala> 

scala> val actor2 = actor { 
    | link(actor1) 
    | self.trapExit = true 
    | loop { 
    |  react { 
    |   case msg => println(msg) 
    |    } 
    |   } 
    | } 
actor2: scala.actors.Actor = [email protected] 

scala> actor1.start 
res12: scala.actors.Actor = [email protected] 

scala> actor2.start 
res13: scala.actors.Actor = [email protected] 

scala> actor1 ! Stop 
Actor 1: stop 

scala> Exit([email protected],'normal) // Actor 2 received message, when Actor1 died 

Una forma más sofisticada y flexible está utilizando supervisores (comportamiento supervisor Erlang, actor supervisors en la biblioteca Akka Actors, etc.). Un supervisor (que a su vez es un actor) monitorea una serie de otros actores y los reinicia con respecto a una estrategia específica (reiniciar todos los actores, si uno muere, reiniciar solo un actor, cuando muera).

+0

vinculación está lejos de ser perfecto. En primer lugar, creo que si intentas vincular a un actor que ya ha salido, no recibirás una notificación. En segundo lugar, la vinculación a un actor que cae antes de que entre en cualquier tipo de bucle de reacción/recepción también pierde cualquier devolución de llamada. –

+0

La vinculación es idiomática. En los dos casos que mencionas, antes de vincular tiene sentido llamar a 'getState' y asegurarte de que el estado de ese actor sea diferente de 'terminado'. –

+0

Vasil, ¿pueden dar más detalles sobre getState? Si puedo verificar si un actor ha terminado, esa también será una buena solución para mí. * editar * De acuerdo, miré este enlace: http://www.scala-lang.org/api/current/index.html, y veo que hay una enumeración. Supongo que randomActorObject.getState == State.Terminated sería el cheque que estoy buscando. – Felix

0

todos bien, me han llegado con una solución usando la función getState de la clase actor. En la solución, utilicé una idea de este hilo: Best method to peek into a Scala Actor's Mailbox en el que se usa reactWithin (0). Me encontré con problemas cuando usaba reaccionar y bucle donde el programa simplemente bloqueaba grandes cálculos. Esto se resolvió reemplazando loop con while (true) y reactionWithin (int) con receiveWithin (int).

Mi solución se ve de la siguiente manera (cuidado, bigass código de grumos):

package test 

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

case class Computation(index: Int, a:() ⇒ Int) 
case class Result(i: String) 
object Main { 
    def main(args: Array[String]) { 
    val m = new Master 
    m.start 
    } 
} 

class Master extends Actor { 

    val N = 40 
    var numberOfAnswers = 0 

    def fib(x: Int): Int = 
    if (x < 2) 
     1 
    else 
     fib(x - 1) + fib(x - 2) 

    val computers = for (i ← 0 to N) yield new Computer 

    def act { 

    for (i ← 0 until computers.size) { 
     computers(i).start 
     computers(i) ! Computation(i,() => fib(i)) 
    } 

    println("done Initializing actors") 
    while (true) { 
     receiveWithin(1000) { 

     case Result(i) => 
      val numberDone = computers.map(_.getState == Terminated).filter(_ == true).length 
      println(i) 
      numberOfAnswers += 1 

     case TIMEOUT => 
      val allDone = computers.map(_.getState == Terminated).reduceRight(_ && _) 
      println("All workers done?:" + allDone) 
      println("# of answers:" + numberOfAnswers) 
      if (allDone) 
      exit() 
     } 
    } 

    } 

} 

class Computer extends Actor { 

    def act { 
    loop { 
     react { 
     case Computation(i, f) ⇒ 
      sender ! Result("#" + i + " Res:" + f()) 
      exit() 
     } 
    } 
    } 

} 

El programa calcula los números de Fibonacci (de la peor manera posible). La idea es simplemente probar la utilización de múltiples hilos para grandes cargas de trabajo. Las siguientes verificaciones en línea si algún actor todavía tiene que terminar:

computers.map(_.getState == Terminated).reduceRight(_ && _) 

donde las computadoras es del tipo IndexedSeq [PC]. El truco es que al usar el mensaje TIMEOUT, puedo verificar periódicamente si todo el trabajo está hecho y actuar en consecuencia (en este caso, salir cuando no haya más trabajadores activos). Aprovecho el hecho de que cada trabajador envía los resultados antes de que salgan. De esa manera sé que siempre recibiré los resultados y los manejaré antes de que se muestren como Terminados.

¿Alguien puede comentar sobre el hecho de que el programa "se bloquea" (deja de recibir mensajes) cuando uso reaccionar y repetir en lugar de while (verdadero) y recibir?

Cuestiones relacionadas