问题
I need an actor to send a message every minute. How do I best achieve this behaviour? I am afraid of using java.lang.Thread.sleep(long millis)
as a thread can be shared among many actors in Scala, as far as I understand.
回答1:
Create an actor with receiveWithin
to act as the timer.
回答2:
Or as @Daniel mentioned, here a running example:
import scala.actors._
import scala.actors.Actor._
class TimerActor(val timeout: Long,val who: Actor,val reply: Any) extends Actor {
def act {
loop {
reactWithin(timeout) {
case TIMEOUT => who ! reply
}
}
}
}
val a = actor {
loop {
react {
case x => println(x)
}
}
}
val t = new TimerActor(1000, a, "Go for it")
a.start
t.start
回答3:
You can use Akka FSM to model an actor that stays forMax
millis in a waiting state and then sends a message, e.g. by switching to another state while using onTransition
and staying there for 0 millis to switch back to waiting state. There is a good example at the akka page.
回答4:
import scala.actors._
class Wakeup[A](millis: Int, who: ReplyReactor, alarm: A) extends Thread {
val done = new java.util.concurrent.atomic.AtomicBoolean(false)
override def run {
while (!done.get()) {
who ! alarm
Thread.sleep(millis)
}
}
}
case object BEEP {}
val a = new ReplyReactor { def act { loop { react {
case BEEP => println("Wha?! "+new java.util.Date)
case _ =>
}}}}
val b = new Wakeup(60000,a,BEEP)
a.start
Why use an actor when a thread is what you want?
scala> b.start
scala> Wha?! Mon Nov 07 18:43:18 EST 2011
Wha?! Mon Nov 07 18:44:18 EST 2011
Wha?! Mon Nov 07 18:45:18 EST 2011
Wha?! Mon Nov 07 18:46:18 EST 2011
Wha?! Mon Nov 07 18:47:18 EST 2011
Wha?! Mon Nov 07 18:48:18 EST 2011
Wha?! Mon Nov 07 18:49:18 EST 2011
Wha?! Mon Nov 07 18:50:18 EST 2011
Wha?! Mon Nov 07 18:51:18 EST 2011
Wha?! Mon Nov 07 18:52:18 EST 2011
回答5:
I ended up in creation of dedicated Runnable instance, which keeps sending a message to the target actor. Like
case class QueueTick()
class QueueWatcherActor extends Actor {
override def receive = {
case QueueTick() => // do it here
}
}
val ref = ActorSystem("xxx")
val actor = ref.actorOf(Props[QueueWatcherActor])
val executor = Executors.newSingleThreadScheduledExecutor()
executor.scheduleAtFixedRate(new Runnable {
def run() {
actor ! QueueTick()
}
},1,60,TimeUnit.SECONDS)
回答6:
Since scala.actors is now deprecated and being replaced with akka actors (and since there is no react or receiveWithin in akka actors), here is how to do it using akka actors (it's actually less of a 'hack' than using receiveWithin anyways IMHO).
The example below schedule a runnable to be invoked after 5 seconds:
import akka.actor.{ActorSystem, Scheduler}
import scala.concurrent.duration.FiniteDuration
import scala.concurrent.ExecutionContext.Implicits.global
class TimerExample {
def example() = {
def scheduler: Scheduler = ActorSystem.create("timer-example").scheduler
val myRunnable = new Runnable {
override def run(): Unit = {
println("run invoked")
}
}
println("scheduling...")
scheduler.scheduleOnce(FiniteDuration(5,TimeUnit.SECONDS),myRunnable)
Thread.sleep(6000)
println("should have printed 'run invoked'")
}
来源:https://stackoverflow.com/questions/8043815/how-to-write-a-timer-actor-in-scala