在创建的未来提交操作

时间:2017-06-11 23:05:08

标签: scala concurrency future

我有一个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

这个命令对我很重要。有没有办法保留这个订单?

当然,我可以使用一个演员,他要求未来并且在未来准备好的时候藏匿字符串,但对我来说这似乎是多余的。

2 个答案:

答案 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)