为什么 Scala Future 运行 映射到 Iterator 时顺序
Why is Scala Future running sequentially when mapped over an Iterator
所以我很困惑为什么用 Futures 映射迭代器会使其顺序 运行。考虑以下代码 -
import org.scalameter._
object IteratorVsListInFutures extends App {
def slowFunction = Thread.sleep(1000)
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent._
import duration._
println("Should take approximately 4000 ms or 4 sec")
println{
withWarmer(new Warmer.Default) measure {
List(1,2,3,4).foreach(_ => slowFunction)
}
}
println("Should take approximately 1 second")
println {
withWarmer(new Warmer.Default) measure {
val futures: Seq[Future[Unit]] = List(1,2,3,4).map(_ => Future { slowFunction})
futures.foreach(x => Await.result(x, 10.seconds))
}
}
println("And how long does this take")
println {
withWarmer(new Warmer.Default) measure {
val futures = List(1,2,3,4).iterator.map(_ => Future { slowFunction})
futures.foreach(x => Await.result(x, 10.seconds))
}
}
}
我得到以下结果 -
Should take approximately 4000 ms or 4 sec
4012.132085 ms
Should take approximately 1 second
1004.997573 ms
And how long does this take
4016.533206 ms
Process finished with exit code 0
第一个benchmark和预测的差不多4秒,第二个benchmark也和预测的差不多1秒(因为future是并行执行的)
令我困惑的是,为什么第三个基准测试也是大约 4 秒?
由于non-strict迭代器的性质,以下
List(1,2,3,4).iterator.map(_ => Future { slowFunction })
评估为 Iterator[Future[Unit]]
这只是对要发生但尚未发生的转换的 描述 。关键是看懂
lazy collections are particularly useful to describe successive
transformation operations without evaluating intermediate
transformations
让我们稍微重写您的示例以强调中间转换
List(1,2,3,4)
.iterator
.map(_ => Future { slowFunction }) // <-- intermediate transformation
.foreach(x => Await.result(x, 10.seconds))
因此 map(_ => Future { slowFunction })
是未评估的中间转换,但这是应该启动期货的转换。相反,map
转换与 foreach
转换结合起来作为一个单独的转换执行,就像这样
List(1,2,3,4)
.foreach(_ => Await.result(Future { slowFunction }, 10.seconds) )
现在很明显,我们正在等待 Future
完成,然后再继续下一个元素,因此 Future
连续完成。
所以我很困惑为什么用 Futures 映射迭代器会使其顺序 运行。考虑以下代码 -
import org.scalameter._
object IteratorVsListInFutures extends App {
def slowFunction = Thread.sleep(1000)
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent._
import duration._
println("Should take approximately 4000 ms or 4 sec")
println{
withWarmer(new Warmer.Default) measure {
List(1,2,3,4).foreach(_ => slowFunction)
}
}
println("Should take approximately 1 second")
println {
withWarmer(new Warmer.Default) measure {
val futures: Seq[Future[Unit]] = List(1,2,3,4).map(_ => Future { slowFunction})
futures.foreach(x => Await.result(x, 10.seconds))
}
}
println("And how long does this take")
println {
withWarmer(new Warmer.Default) measure {
val futures = List(1,2,3,4).iterator.map(_ => Future { slowFunction})
futures.foreach(x => Await.result(x, 10.seconds))
}
}
}
我得到以下结果 -
Should take approximately 4000 ms or 4 sec
4012.132085 ms
Should take approximately 1 second
1004.997573 ms
And how long does this take
4016.533206 ms
Process finished with exit code 0
第一个benchmark和预测的差不多4秒,第二个benchmark也和预测的差不多1秒(因为future是并行执行的) 令我困惑的是,为什么第三个基准测试也是大约 4 秒?
由于non-strict迭代器的性质,以下
List(1,2,3,4).iterator.map(_ => Future { slowFunction })
评估为 Iterator[Future[Unit]]
这只是对要发生但尚未发生的转换的 描述 。关键是看懂
lazy collections are particularly useful to describe successive transformation operations without evaluating intermediate transformations
让我们稍微重写您的示例以强调中间转换
List(1,2,3,4)
.iterator
.map(_ => Future { slowFunction }) // <-- intermediate transformation
.foreach(x => Await.result(x, 10.seconds))
因此 map(_ => Future { slowFunction })
是未评估的中间转换,但这是应该启动期货的转换。相反,map
转换与 foreach
转换结合起来作为一个单独的转换执行,就像这样
List(1,2,3,4)
.foreach(_ => Await.result(Future { slowFunction }, 10.seconds) )
现在很明显,我们正在等待 Future
完成,然后再继续下一个元素,因此 Future
连续完成。