我有一个Future lazy val,它获取了一些对象和一个在Future中提交操作的函数。
class C {
def printLn(s: String) = println(s)
}
lazy val futureC: Future[C] = Future{Thread.sleep(3000); new C()}
def func(s: String): Unit = {
futureC.foreach{c => c.printLn(s)}
}
问题是当Future完成后,它以相反的顺序执行操作而不是提交。例如,如果我执行sequentialy
func("A")
func("B")
func("C")
我在完成未来之后
scala> C
B
A
这个命令对我很重要。有没有办法保留这个订单?
当然,我可以使用一个演员,他要求未来并且在未来准备好的时候藏匿字符串,但对我来说这似乎是多余的。
答案 0 :(得分:0)
lazy val futureC: Future[C]
scala中的 lazy val
将被编译到代码中,该代码使用synchronized块来保证线程安全。
这里调用func(A)
时,它将获得lazy val
的锁定,该线程将进入休眠状态。
因此func(B)
&锁定会阻止func(C)
。
当运行这些被阻止的线程时,无法保证订单。
如果您按照以下方式执行此操作,您将按预期获得订单。这是因为for
理解会创建flatMap
,&基于map
的链,按顺序执行。
lazy val futureC: Future[C] = Future {
Thread.sleep(1000)
new C()
}
def func(s: String) : Future[Unit] = {
futureC.map { c => c.printLn(s) }
}
val x = for {
_ <- func("A")
_ <- func("B")
_ <- func("C")
} yield ()
即使没有lazy
关键字,订单也会保留。除非确实有必要,否则您可以删除lazy
关键字。
希望这有帮助。
答案 1 :(得分:-1)
您可以使用Future.traverse
来确保执行顺序。
这样的东西..我不确定你的func如何引用正确的futureC,所以我把它移到了里面。
def func(s: String): Future[Unit] = {
lazy val futureC = Future{Thread.sleep(3000); new C()}
futureC.map{c => c.printLn(s)}
}
def traverse[A,B](xs: Seq[A])(fn: A => Future[B]): Future[Seq[B]] =
xs.foldLeft(Future(Seq[B]())) { (acc, item) =>
acc.flatMap { accValue =>
fn(item).map { itemValue =>
accValue :+ itemValue
}
}
}
traverse(Seq("A","B","C"))(func)