Reputation: 16789
I have a cache I want to periodically check and prune. In Java, I'd do the following:
new Thread(new Runnable() {
void run() {
while (true) {
Thread.sleep(1000);
// clear the cache's old entries
}
}
}).start();
Sure, I'd have some issues with thread-safe types to use as the cache, but putting that aside, my question is simple. What's the Scala way of running a recurring background task -- something you don't want running in the application's main thread?
I've used actors a bit and I guess my problem in this scenario is that I don't have anything to generate a message that it's time to clear the cache. Or rather, the only way I can imagine to generate those messages is to create a thread to do it...
EDIT: I need people to vote on answers -- they all look good to me
Upvotes: 33
Views: 15634
Reputation: 18024
As of Scala 2.11.x, Akka actors seems to be the best way to do this IMHO. First create a task scheduling library:
import akka.actor.ActorSystem
import scala.language.postfixOps
import scala.concurrent.duration._
val actorSystem = ActorSystem()
val scheduler = actorSystem.scheduler
implicit val executor = actorSystem.dispatcher
// do once after period millis
def doOnce(fn: => Unit, period:Long) = scheduler.scheduleOnce(period milliseconds)(fn)
// do regularly every period millis starting now
def doRegularly(fn: => Unit, period:Long) = scheduler.schedule(0 seconds, period milliseconds)(fn)
// do regularly every period millis if whileFn is true, starting now
def doWhile(fn: => Unit, whileFn: => Boolean, period:Long) {
if (whileFn) {
fn
doOnce(doWhile(fn, whileFn, period), period)
}
}
Then use this as:
doRegularly({
println("hello world!")
}, 1000) // repeat every 1000 millisecs
doWhile({
println("Sleeping!")
}, whileDaylight, 1000) // repeat every 1000 millisecs whileDaylight is true
Upvotes: 4
Reputation: 45408
spawn
is good but note that your sample code works in Scala too:
new Thread(new Runnable() {
override def run() {
while (true) {
Thread.sleep(1000);
// clear the cache's old entries
}
}
}).start();
Just clean it up with an implicit conversion:
implicit def funcToRunnable(f: => ()) = new Runnable() { override def run() { f() } }
new Thread{
while(true) {
Thread.sleep(1000);
// blah
}
}.start()
Upvotes: 7
Reputation: 40461
You could use Akka Scheduler, which allows you to send a reccuring message to an (akka) actor doing the job. From the doc, just use:
import akka.actor.Scheduler
//Sends messageToBeSent to receiverActor after initialDelayBeforeSending and then after each delayBetweenMessages
Scheduler.schedule(receiverActor, messageToBeSent, initialDelayBeforeSending, delayBetweenMessages, timeUnit)
Upvotes: 11
Reputation: 1188
With Actors without tying up a thread:
import actors.{TIMEOUT, Actor}
import actors.Actor._
private case class Ping( client: Actor, update: Int )
private case class Pulse()
case class Subscribe( actor: Actor )
case class Unsubscribe( actor: Actor )
class PulseActor extends Actor {
def act = eventloop {
case ping: Ping => { sleep(ping.update); ping.client ! Pulse }
}
def sleep(millis: Long) =
receiveWithin(millis) {
case TIMEOUT =>
}
}
class ServiceActor extends Actor {
var observers: Set[Actor] = Set.empty
val pulseactor = new PulseActor
val update = 2000
def act = {
pulseactor.start
pulseactor ! new Ping( this, update )
loop {
react {
case sub: Subscribe => observers += sub.actor
case unsub: Unsubscribe => observers -= unsub.actor
case Pulse => pulse
}
}
}
def pulse {
//cpuload = CPUprofile.getCPUload.getOrElse{ List(0.0) } //real work
observers foreach { observer => observer ! "CPUloadReport( cpuload )" }
pulseactor ! Ping(this, update)
}
}
object Exercise extends App {
val deamon = new ServiceActor
deamon.start
}
Upvotes: 5
Reputation: 18024
Futures is a simple way to do it without explicitly starting a new thread
import scala.actors.Futures._
// main thread code here
future {
// second thread code here
}
// main thread code here
Upvotes: 7
Reputation: 431
There are many ways to do that, but I would do something simple like the following.
import scala.concurrent.ops._
spawn {
while (true) {
Thread.sleep(1000);
// clear the cache's old entries
}
}
Hope this helps.
Upvotes: 26