问题: 我正在尝试在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()
}
答案 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。