我想在一段时间内轮询一个数据库。为了避免服务器负载,主要问题是避免重叠运行以及在时间轴上运行不均匀;否则,天真的计时器方案会发生这种情况。
不同之处:我希望在一个运行结束和下一个结束的开始时间之间有一个固定的间隔。
试图避免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)
}
有什么建议吗?
线程池看起来很健康:
另外,我应该更好地为这个正在进行的过程使用某种单独的执行上下文,而不是使用blocking
成语来搭载默认的上下文吗?
谢谢!
答案 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异常会更好。