2009-08-03 14 views
15

¿Cuál es la mejor manera de que un actor duerma? Tengo actores establecidos como agentes que desean mantener diferentes partes de una base de datos (incluida la obtención de datos de fuentes externas). Por una serie de razones (que incluyen no sobrecargar la base de datos o las comunicaciones y problemas generales de carga), quiero que los actores duerman entre cada operación. Estoy viendo algo así como 10 objetos de actor.¿Actores dormidos?

Los actores se ejecutarán bastante infinitamente, ya que siempre habrá nuevos datos entrantes, o sentados en una mesa esperando a ser propagados a otras partes de la base de datos, etc. La idea es que la base de datos sea tan completa como posible en cualquier punto en el tiempo.

Podría hacer esto con un bucle infinito y un descanso al final de cada ciclo, pero de acuerdo con http://www.scala-lang.org/node/242 los actores usan un grupo de hilos que se expande cada vez que se bloquean todos los hilos. Así que me imagino que un hilo de dormir en cada actor sería una mala idea, ya que desperdiciaría los hilos innecesariamente.

¿Podría tener un actor central con su propio bucle que envía mensajes a los suscriptores en un reloj (como observadores de reloj de eventos asincrónicos)?

¿Alguien ha hecho algo similar o tiene alguna sugerencia? Perdón por información adicional (tal vez superflua).

Saludos

Joe

Respuesta

16

No hay necesidad de hacer explícita un actor para dormir: usando loop y react para cada actor significa que el grupo de subprocesos subyacente tendrá subprocesos en espera, mientras que no hay mensajes para los actores para procesar.

En el caso que desee horario eventos para sus actores a proceso, esto es bastante fácil usando un único subproceso planificador desde los java.util.concurrent utilidades:

object Scheduler { 
    import java.util.concurrent.Executors 
    import scala.compat.Platform 
    import java.util.concurrent.TimeUnit 
    private lazy val sched = Executors.newSingleThreadScheduledExecutor(); 
    def schedule(f: => Unit, time: Long) { 
    sched.schedule(new Runnable { 
     def run = f 
    }, time , TimeUnit.MILLISECONDS); 
    } 
} 

Se podría extender esto a tomar periódica tareas y que podría ser utilizado por lo tanto:

val execTime = //... 
Scheduler.schedule({ Actor.actor { target ! message };() }, execTime) 

Su agente objetivo entonces simplemente hay que implementar un bucle react adecuada para procesar el mensaje dado. No es necesario que tengas ningún actor dormido.

21

Hubo un buen punto para Erlang en la primera respuesta, pero parece desaparecido. Puedes hacer el mismo truco parecido a Erlang con los actores de Scala fácilmente. P.ej. vamos a crear un planificador que no utiliza hilos:

import actors.{Actor,TIMEOUT} 

def scheduler(time: Long)(f: => Unit) = { 
    def fixedRateLoop { 
    Actor.reactWithin(time) { 
     case TIMEOUT => f; fixedRateLoop 
     case 'stop => 
    } 
    } 
    Actor.actor(fixedRateLoop) 
} 

Y vamos a probarlo (lo hice bien en Scala REPL) usando un agente cliente de prueba:

case class Ping(t: Long) 

import Actor._ 
val test = actor { loop { 
    receiveWithin(3000) { 
    case Ping(t) => println(t/1000) 
    case TIMEOUT => println("TIMEOUT") 
    case 'stop => exit 
    } 
} } 

Ejecutar el planificador:

import compat.Platform.currentTime 
val sched = scheduler(2000) { test ! Ping(currentTime) } 

y verá algo como esto

scala> 1249383399 
1249383401 
1249383403 
1249383405 
1249383407 

lo que significa que nuestro planificador envía un mensaje cada 2 segundos como se esperaba.Vamos a detener el planificador:

sched ! 'stop 

el cliente de prueba comenzará a reportar los tiempos de espera:

scala> TIMEOUT 
TIMEOUT 
TIMEOUT 

parada es así:

test ! 'stop 
+2

+1 para mostrar las importaciones y tener un ejemplo de ejecución de REPL. – opyate

+0

+1 este truco funciona perfecto –

4

ActorPing (licencia Apache) de elevación-util tiene cronograma y programaciónFuente corregida Origen: ActorPing.scala

De scaladoc:

El objeto ActorPing programa a un actor para que se le envíe un mensaje determinado a intervalos específicos. Los métodos de programación devuelven un objeto ScheduledFuture que se puede cancelar si es necesario

2

Desafortunadamente hay dos errores en la respuesta de oxbow_lakes.

Uno es un simple error de declaración (tiempo prolongado vs tiempo: largo), pero el segundo es un poco más sutil.

oxbow_lakes declara ejecutan como

def run = actors.Scheduler.execute(f) 

Sin embargo, esto conduce a la desaparición de los mensajes de vez en cuando. Es decir: están programados pero nunca se envían. Declarando ejecutar como

def run = f 

lo arregló para mí. Se hace de la manera exacta en ActorPing de lift-util.

El código se convierte en toda planificador:

object Scheduler { 
    private lazy val sched = Executors.newSingleThreadedScheduledExecutor(); 
    def schedule(f: => Unit, time: Long) { 
     sched.schedule(new Runnable { 
      def run = f 
     }, time - Platform.currentTime, TimeUnit.MILLISECONDS); 
    } 
} 

traté de editar oxbow_lakes puesto, pero no podía guardarlo (roto?), No tengo derecho a hacer comentarios, todavía. Por lo tanto, una nueva publicación.

+0

He editado su publicación, con sus correcciones y la mía. –

Cuestiones relacionadas