Scala调度运行之间的固定间隔

时间:2016-07-13 07:58:19

标签: scala akka

我想在一段时间内轮询一个数据库。为了避免服务器负载,主要问题是避免重叠运行以及在时间轴上运行不均匀;否则,天真的计时器方案会发生这种情况。

不同之处:我希望在一个运行结束和下一个结束的开始时间之间有一个固定的间隔。

试图避免Actors只是因为它们带来的代码样板,我在下面尝试过这个。也许有更好或更简单的方式。

  /* 
   * Infinite scheduling for acquiring a database snapshot at an almost fixed interval.
   * The actual effective acquisition interval is (duration of the acquisition + dbSyncIntervalSecs)  
   */

  import scala.concurrent.duration._
  import scala.concurrent.blocking
  import scala.concurrent.Future
  import scala.concurrent.ExecutionContext.Implicits.global

  val system = ActorSystem("MySystem") // make global if more things hinge on the actor system

  val interval = Configuration.dbSyncIntervalSecs

  private def acquireLoop: Unit = {
    Future { 
      blocking { 
        println("about to sync from database")
        acquire
      }
    }
    system.scheduler.scheduleOnce(interval seconds)(acquireLoop)
  }

有什么建议吗?

线程池看起来很健康:

enter image description here

另外,我应该更好地为这个正在进行的过程使用某种单独的执行上下文,而不是使用blocking成语来搭载默认的上下文吗?

谢谢!

3 个答案:

答案 0 :(得分:4)

虽然你已经说过你不想因为样板文件,但使用scheduleOnce的一个简单的演员是一个很好的方法,并且样板文件看起来并不那么糟糕:

import akka.actor.{Actor, Props, ActorSystem}
import scala.concurrent.duration._

val system = ActorSystem("MySystem")
val interval = 3 seconds
val actor = system.actorOf(Props(new Actor{
  override def preStart = self ! "Execute"
  override def receive: Receive = {
    case "Execute" =>
      println(s"Executing at ${System.currentTimeMillis()}")
      context.system.scheduler.scheduleOnce(interval, self, "Execute")(context.dispatcher)
  }}))

很少的代码行可以获得所需的确切结果。

答案 1 :(得分:2)

“正常”schedule method保证您想要 - 提交的=> Unit块仅以一次一个方式运行。

引用文档:

  

安排一个函数以初始延迟和a重复运行   频率。例如。如果你想在2之后运行该功能   几秒钟,然后每100毫秒你会设置延迟=持续时间(2,   TimeUnit.SECONDS)和interval = Duration(100,TimeUnit.MILLISECONDS)。   如果函数的执行时间超过了间隔,则   后续执行将在前一次执行后立即开始   完成(函数执行不会重叠)

(emph mine)

编辑:你现在提到你希望在运行 之间的固定时间以及 。好吧,一个权宜之计是简单地使用Java API的ScheduledExecutorService#scheduleWithFixedDelay

  

创建并执行首先启用的定期操作   在给定的初始延迟之后,以及随后的给定延迟   在一次执行的终止和一次执行的开始之间   下。

(emph mine)

不幸的是,没有直接等效于此方法 - 既不在Scala库中,也不在Akka Scheduler API中。请注意,使用情况需要您将任务定义为Runnable,但这应该不是问题。

(就个人而言,我会使用一个简单的演员scheduleOnce,但你提到你想要避开它们)

答案 2 :(得分:0)

  

不同之处:我希望在跑步结束和下一个开始时间之间有一个固定的间隔。

嗯,所以你只想:

while(true) {
    doThing()
    Thread.sleep(fixedTimeBetweenEndAndStart)
}

在尾递归函数中使用它并通过正确处理Thread.isInterrupted / interrupt异常会更好。