2010-04-02 14 views
8

Estoy seguro de que esta es una pregunta muy simple, pero vergüenza decir que no puedo conseguir mi cabeza alrededor de ella:simple Scala pregunta el actor

tengo una lista de valores en Scala. Me gustaría usar actores de uso para hacer algunas llamadas (externas) con cada valor, en paralelo.

Me gustaría esperar hasta que se hayan procesado todos los valores, y luego proceder.

No se han modificado los valores compartidos.

¿Alguien podría aconsejar?

Gracias

Respuesta

17

Hay una clase de actor y el uso de Scala que ha hecho precisamente por este tipo de problema: Futuros. Este problema se resolvería así:

// This assigns futures that will execute in parallel 
// In the example, the computation is performed by the "process" function 
val tasks = list map (value => scala.actors.Futures.future { process(value) }) 

// The result of a future may be extracted with the apply() method, which 
// will block if the result is not ready. 
// Since we do want to block until all results are ready, we can call apply() 
// directly instead of using a method such as Futures.awaitAll() 
val results = tasks map (future => future.apply()) 

Ahí lo tiene. Solo eso.

+1

Esto es demasiado simple, debe haber algo mal con él ;-) Se ve muy bien, gracias de nuevo. – 7zark7

+0

@ 7zark7 Sí, me sentí de esa manera también. :-) Se compila, sin embargo, y no creo que haya cometido ningún error en particular. Asegúrese de que 'process' no arroje excepciones, y tenga en cuenta que si va a hacer I/O, puede que no funcione tan bien como esperaba, ya que los futuros en ejecución simplemente bloquearán los hilos en espera. –

+1

Funciona. De hecho, lo probé para asegurarme de que realizaba tantas tareas al mismo nivel que el estilo de actor (tenía curiosidad, ya que no lo había hecho antes o había investigado el código fuente 'Futures'), y lo hace. –

9

Crear trabajadores y pedirles futuros utilizando !!; luego lea los resultados (que se calcularán y aparecerán en paralelo cuando estén listos; luego puede usarlos). Por ejemplo:

object Example { 
    import scala.actors._ 
    class Worker extends Actor { 
    def act() { Actor.loop { react { 
     case s: String => reply(s.length) 
     case _ => exit() 
    }}} 
    } 
    def main(args: Array[String]) { 
    val arguments = args.toList 
    val workers = arguments.map(_ => (new Worker).start) 
    val futures = for ((w,a) <- workers zip arguments) yield w !! a 
    val results = futures.map(f => f() match { 
     case i: Int => i 
     case _ => throw new Exception("Whoops--didn't expect to get that!") 
    }) 
    println(results) 
    workers.foreach(_ ! None) 
    } 
} 

Esto hace un cálculo muy barato - el cálculo de la longitud de una cadena - pero se puede poner algo caro allí para asegurarse de que realmente sucede en paralelo (la última cosa que el caso de la el bloque del acto debe ser responder con la respuesta). Tenga en cuenta que también incluimos un caso para que el trabajador se cierre solo, y cuando terminemos, les decimos a los trabajadores que se apaguen. (En este caso, cualquier no-cadena apaga el trabajador.)

y podemos probar esto para asegurarse de que funciona:

scala> Example.main(Array("This","is","a","test")) 
List(4, 2, 1, 4) 
+0

Muchas gracias - ¡Parecía haber perdido por completo la mención de los futuros y el uso de! antes de esto. – 7zark7

+0

Debo señalar que tal vez estaba siendo demasiado literal con el uso de actores: si no va a reutilizarlos, la respuesta de Daniel es aún más corta y más directa para llegar al cómputo paralelo que solicitó. (Es decir, usar futuros sin actores.) –

+0

@Mog - 'f()' obtiene el resultado del cálculo (bloques hasta que esté disponible). El 'match' se debe a que los mensajes enviados por los actores se escriben como' Any'; esperamos obtener un int atrás (así que lo buscamos), pero si por alguna razón inesperada obtenemos algo más, lanzamos una excepción . –