Подтвердить что ты не робот

Фоновая задача в Scala

У меня есть кеш, который я хочу периодически проверять и обрезать. В Java я бы сделал следующее:

new Thread(new Runnable() {
  void run() {
    while (true) { 
      Thread.sleep(1000);
      // clear the cache old entries
    }
  }
}).start();

Конечно, у меня возникнут проблемы с потокобезопасными типами, которые будут использоваться в качестве кеша, но, отложив это, мой вопрос прост. Какой способ Scala запуска повторяющейся фоновой задачи - что-то, чего вы не хотите запускать в главном потоке приложения?

Я немного использовал актеров, и я думаю, что моя проблема в этом сценарии заключается в том, что мне нечего генерировать сообщение о том, что время для очистки кеша. Вернее, единственный способ представить эти сообщения - создать поток, чтобы сделать это...

РЕДАКТИРОВАТЬ: мне нужно, чтобы люди голосовали по ответам - все они мне нравятся

4b9b3361

Ответ 1

Есть много способов сделать это, но я бы сделал что-то простое, как следующее.

import scala.concurrent.ops._

spawn {
  while (true) { 
    Thread.sleep(1000);
    // clear the cache old entries
  }
}

Надеюсь, что это поможет.

Ответ 2

Вы можете использовать Akka Scheduler, который позволяет отправлять сообщение о реферировании актеру (акка), выполняющему задание. Из документа просто используйте:

import akka.actor.Scheduler

//Sends messageToBeSent to receiverActor after initialDelayBeforeSending and then after each delayBetweenMessages
Scheduler.schedule(receiverActor, messageToBeSent, initialDelayBeforeSending, delayBetweenMessages, timeUnit)

Ответ 3

spawn хорош, но обратите внимание, что ваш пример кода работает и в Scala:

new Thread(new Runnable() {
  override def run() {
    while (true) { 
      Thread.sleep(1000);
      // clear the cache old entries
    }
  }
}).start();

Просто очистите его с неявным преобразованием:

implicit def funcToRunnable(f: => ()) = new Runnable() { override def run() { f() } }

new Thread{
  while(true) {
    Thread.sleep(1000);
    // blah
  }
}.start()

Ответ 4

Фьючерсы - это простой способ сделать это без явного запуска нового потока

import scala.actors.Futures._

// main thread code here

future {
   // second thread code here
}

// main thread code here

Ответ 5

С Актерами без привязки потока:

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
}

Ответ 6

По состоянию на Scala 2.11.x актеры Akka, похоже, лучший способ сделать это IMHO. Сначала создайте библиотеку планирования задач:

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)
 }
}

Затем используйте это как:

doRegularly({
  println("hello world!")
}, 1000) // repeat every 1000 millisecs

doWhile({
  println("Sleeping!")
}, whileDaylight, 1000) // repeat every 1000 millisecs whileDaylight is true