Scala-计划的未来
我正在尝试在Scala中实现预定的未来。我希望它等待特定的时间,然后执行身体。到目前为止,我尝试了以下简单的方法Scala-计划的未来,scala,future,Scala,Future,我正在尝试在Scala中实现预定的未来。我希望它等待特定的时间,然后执行身体。到目前为止,我尝试了以下简单的方法 val d = 5.seconds.fromNow val f = future {Await.ready(Promise().future, d.timeLeft); 1} val res = Await.result(f, Duration.Inf) 但我对未来有时间限制。这是正确的方法还是我应该简单地使用Java中的ScheduledExecutor?您可以将代码更改为以
val d = 5.seconds.fromNow
val f = future {Await.ready(Promise().future, d.timeLeft); 1}
val res = Await.result(f, Duration.Inf)
但我对未来有时间限制。这是正确的方法还是我应该简单地使用Java中的ScheduledExecutor?您可以将代码更改为以下内容:
val d = 5.seconds.fromNow
val f = Future {delay(d); 1}
val res = Await.result(f, Duration.Inf)
def delay(dur:Deadline) = {
Try(Await.ready(Promise().future, dur.timeLeft))
}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits._
DelayedFuture( 5 seconds )( println("Hello") )
但我不推荐。这样做,您将在将来阻塞(阻塞以等待永远不会完成的
承诺),我认为执行上下文中的阻塞是非常不受鼓励的。我可以按照您所说的使用java scheduled executor,也可以按照@alex23的建议使用Akka。仅使用标准库是无法做到这一点的。
对于大多数简单的用例,您可以使用如下小助手:
object DelayedFuture {
import java.util.{Timer, TimerTask}
import java.util.Date
import scala.concurrent._
import scala.concurrent.duration.FiniteDuration
import scala.util.Try
private val timer = new Timer(true)
private def makeTask[T]( body: => T )( schedule: TimerTask => Unit )(implicit ctx: ExecutionContext): Future[T] = {
val prom = Promise[T]()
schedule(
new TimerTask{
def run() {
// IMPORTANT: The timer task just starts the execution on the passed
// ExecutionContext and is thus almost instantaneous (making it
// practical to use a single Timer - hence a single background thread).
ctx.execute(
new Runnable {
def run() {
prom.complete(Try(body))
}
}
)
}
}
)
prom.future
}
def apply[T]( delay: Long )( body: => T )(implicit ctx: ExecutionContext): Future[T] = {
makeTask( body )( timer.schedule( _, delay ) )
}
def apply[T]( date: Date )( body: => T )(implicit ctx: ExecutionContext): Future[T] = {
makeTask( body )( timer.schedule( _, date ) )
}
def apply[T]( delay: FiniteDuration )( body: => T )(implicit ctx: ExecutionContext): Future[T] = {
makeTask( body )( timer.schedule( _, delay.toMillis ) )
}
}
可以这样使用:
val d = 5.seconds.fromNow
val f = Future {delay(d); 1}
val res = Await.result(f, Duration.Inf)
def delay(dur:Deadline) = {
Try(Await.ready(Promise().future, dur.timeLeft))
}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits._
DelayedFuture( 5 seconds )( println("Hello") )
请注意,与java scheduled futures不同,此实现不允许您取消未来。我的解决方案与Régis非常相似,但我使用Akka来安排:
def delayedFuture[T](delay: FiniteDuration)(block: => T)(implicit executor : ExecutionContext): Future[T] = {
val promise = Promise[T]
Akka.system.scheduler.scheduleOnce(delay) {
try {
val result = block
promise.complete(Success(result))
} catch {
case t: Throwable => promise.failure(t)
}
}
promise.future
}
Akka有Akka.pattern:
def after[T](duration: FiniteDuration, using: Scheduler)(value: ⇒ Future[T])(implicit ec: ExecutionContext): Future[T]
“返回一个scala.concurrent.Future,在指定的持续时间后,该值将在提供的值成功或失败的情况下完成。”
最短的解决方案可能是使用scala async:
import scala.async.Async.{async, await}
def delay[T](value: T, t: duration): Future[T] = async {
Thread.sleep(t.toMillis)
value
}
或者,如果您希望延迟执行块
def delay[T](t: duration)(block: => T): Future[T] async {
Thread.sleep(t.toMillis)
block()
}
所有其他解决方案在每个延迟任务中使用akka或阻塞线程。更好的解决方案(除非您已经在使用akka)是使用java的ScheduledThreadPoolExecutor。下面是一个scala包装器的示例:
如果您希望在没有Akka的情况下安排完成,可以使用常规Java计时器安排完成承诺:
def delay[T](delay: Long)(block: => T): Future[T] = {
val promise = Promise[T]()
val t = new Timer()
t.schedule(new TimerTask {
override def run(): Unit = {
promise.complete(Try(block))
}
}, delay)
promise.future
}
Await.ready
使用阻塞
,因此,如果您在五秒钟内进行工作,至少底层池可以为它启动一个线程。此(或原始问题)工作所需的导入将很好地看到。我认为它们是scala.concurrent.duration.u和scala.concurrent.u这可以在没有参与者系统的情况下使用吗?真的不用说为什么这对并发性很好这是useless@matanster你是怎么想的?当然,但这不是问题的一部分。一个如何使用的示例将是handle。这叫什么?作为调度程序应该通过什么?我的回答显示了如何在没有akka或阻止任何线程的情况下完成它,并在一年多前发布。谢谢我一定错过了你的答案,我喜欢它,在我的解决方案中,很少需要执行线程池。这是一个非常不充分的解决方案。如前所述,这将阻止线程。有两件事:在这两种情况下,您是否都缺少嵌套的wait{…}?这将阻止线程阻塞并导致实际的延迟。或者,为什么不用Thread.sleep(…)调用包装在阻塞{…}块中来构造Future,以防止潜在的死锁?我不明白一件事:有一个隐式ctx:ExecutionContext
参数要应用,但是我看不到它会被用在哪里-我看不到makeTask
或timer.schedule
期待它。我有一个更严重的问题,实际上我确实需要传递ExecutionContext。罪魁祸首就在别处:我应该在ExecutionContext上运行body,而不是在计时器的后台线程上运行它(这意味着在整个应用程序范围内,对于每个延迟的未来,实际上只有一个线程,这并不好)。即使它本质上只是一个例子,但对我来说也是草率的。新版本更适合生产。不如使用def run():Unit=prom.complete(Try(block())
代替手动异常包装?Scala()中支持一次您的解决方案有望变得更加清晰和简短。我注意到,private val timer=new timer
使用默认值,即不作为deamon线程运行,因此可能会使用此代码停止进程的关闭以安排偶尔的任务?也许最好使用private val timer=new timer(true)
来防止进程挂起?您确定语法吗?我认为block
不是一个函数,它不应该用作block()
,而应该用作block
。此外,与我的评论类似-你是否在某处使用executor
。@Suma这就是我不用测试就可以从内存中编写代码的原因。我已经修复了代码,并确保它符合我声称的那样,每次创建一个新的计时器实例可能不是一个好主意