Scala Future在块内调用惰性方法不起作用

时间:2019-04-15 06:42:57

标签: scala

问题: 我正在尝试在Future块内调用惰性函数(分配给lazy val的函数),它的行为不符合预期,但是当我直接在该块内执行该函数时,它就按预期运行。不知道是否缺少任何东西。

工作代码: 下面是当我直接在Future块内执行该方法时代码按预期工作的代码

 implicit val ec = ExecutionContext.fromExecutorService {
    Executors.newFixedThreadPool(8)
  }

  def execute1() = {
    Thread.sleep(4000); println("Completed 1!!!")
    1
  }
  def execute2() = {
    Thread.sleep(3000); println("Completed 2!!!")
    2
  }
  def execute3() = {
    Thread.sleep(2000); println("Completed 3!!!")
    3
  }
  def execute4() = {
    Thread.sleep(1000);  println("Completed 4!!!")
    4
  }
  val future1 : Future[Int] = Future.apply(execute1())
  val future2 : Future[Int] = Future.apply(execute2())
  val future3 : Future[Int] = Future.apply(execute3())
  val future4 : Future[Int] = Future.apply(execute4())

  val result = for { r1 <- future1
       r2 <- future2
       r3 <- future3
       r4 <- future4

  } yield {
    println(r1+","+r2+","+r3+","+r4)
  }
  StdIn.readLine()
  sys.exit()

执行上述代码后,将按预期的顺序执行方法“ execute4,execute3,execute2,execute1”。

无效代码: 在上面的代码中,当我尝试将“ execute”方法分配给惰性变量并在Future块中引用该变量时,其行为将有所不同。它以1,4,3,2的顺序执行。请参见下面的代码

  implicit val ec = ExecutionContext.fromExecutorService {
    Executors.newFixedThreadPool(8)
  }

  def execute1() = {
    Thread.sleep(4000); println("Completed 1!!!")
    1
  }
  def execute2() = {
    Thread.sleep(3000); println("Completed 2!!!")
    2
  }

  def execute3() = {
    Thread.sleep(2000); println("Completed 3!!!")
    3
  }

  def execute4() = {
    Thread.sleep(1000); println("Completed 4!!!")
    4
  }

  lazy val e1 = execute1()
  lazy val e2 = execute2()
  lazy val e3 = execute3()
  lazy val e4 = execute4()

  val future1 : Future[Int] = Future.apply(e1)
  val future2 : Future[Int] = Future.apply(e2)
  val future3 : Future[Int] = Future.apply(e3)
  val future4 : Future[Int] = Future.apply(e4)

  val result = for { r1 <- future1
       r2 <- future2
       r3 <- future3
       r4 <- future4

  } yield {
    println(r1+","+r2+","+r3+","+r4)
  }

  StdIn.readLine()
  sys.exit()

预期的行为:由于函数(e1,e2,e3,e4)被称为Lazy,因此应在调用时在Future块内执行它,并且其行为应与工作代码相同。我注意到奇怪的行为是它同步执行execute1()方法,而其余方法异步执行。任何指导或建议对我来说都是很棒的。

我期望的输出: 不管“我在Future块内执行方法”(或)“使方法在Future块外变为惰性并在Future块内调用”都应产生相同的结果。按照我的示例,我期望的输出是“方法执行顺序(异步)为execute4(),execute3(),execute2()和execute(1)的顺序”

为简化示例。 在以下两种方法中,未来执行是不同的。在两种方法中,我都期望相同的输出

//Approach#1 
def method() = {
}

Future{
 method()
}

//Approach#2
lazy val lazyMethod = method()

Future {
    lazyMethod()
}

1 个答案:

答案 0 :(得分:0)

实际上,该代码正在按预期工作。让我解释一下。

首先出现for

val result = for {
  r1 <- future1
  r2 <- future2
  r3 <- future3
  r4 <- future4
} yield {
  println(r1+","+r2+","+r3+","+r4)
}

您正在粗暴地做

val result = 
  future1.flatMap(r1 =>
    future2.flatMap(r2 =>
      future3.flatMap(r3 =>
        future4.map(r4 =>
          println(r1+","+r2+","+r3+","+r4)
        )
      )
    )
  )

这意味着您只有在访问上一个期货的值之后,才能“访问”这些期货计算的值。

现在出现了Future.apply,它以body: => T作为参数并给您一个Future[T],但事实是,这个body将在您创建未来。

因此,在您执行的第一个实现中,

val future1 : Future[Int] = Future.apply(execute1())
val future2 : Future[Int] = Future.apply(execute2())
val future3 : Future[Int] = Future.apply(execute3())
val future4 : Future[Int] = Future.apply(execute4())

所有这些futuresI's开始执行您的executeI's。因此,println中的executeI's将在此后执行x ms,无论您何时尝试访问这些期货中的任何价值。

现在,lazy来了。因此,当您声明这样的内容时,

val laxy x = {
  println("accessing lazy x")
  5
}

仅当您首次访问x时,才会执行该块。

当您这样做时,

val future1 : Future[Int] = Future.apply(e1)
val future2 : Future[Int] = Future.apply(e2)
val future3 : Future[Int] = Future.apply(e3)
val future4 : Future[Int] = Future.apply(e4)

您仍然没有“访问”这些lazy eI's中的任何一个,但是您知道每个未来都会在创建后立即开始计算。因此,当这些期货开始执行时,它们将“访问”这些eI's

为了更好地理解它,让我们如下更改executeI's

def execute1() = {
  println("Started 1!!! " + System.currentTimeMillis())
  Thread.sleep(4000)
  println("Completed 1!!! " + System.currentTimeMillis())
  1
}

您会注意到所有这些eI's都在按顺序执行。

这是因为所有这些eI's将在定义它们的线程中进行评估,而不是在执行Future的线程中进行评估。因此,这些Thread.sleep将阻塞当前线程,并以不确定的顺序(由于某些可能的优化)进行评估,巧合的是碰巧是1、4、3、2。

但是如果您将未来的顺序更改为

val future1 : Future[Int] = Future.apply(e1)
val future4 : Future[Int] = Future.apply(e4)
val future2 : Future[Int] = Future.apply(e2)
val future3 : Future[Int] = Future.apply(e3)

它将变为1、3、2、4。