无法理解并行计算列表元素的正确方法,但在不计算元素(并行)时阻塞主线程。使用案例:我有一个URL链接列表和一个简单的html页面解析器,我可以通过并行解析每个页面来减少从给定页面获取信息所需的时间,然后返回一个带有一些JSON数据的简单列表
据我所知,我有两个选择:
与期货并行
我有一个方法可以在Future中提取一些JSON数据:
def extractData(link: String): Future[JValue] = // some implementation
我只是将它映射到一个链接列表,其类型为List [Future [JValue]]:
val res: List[Future[JValue]] = listOfLink.map(extractData)
如果我调用sequence
(例如来自Scalaz或我自己的实现)遍历此列表并将其转换为Future[List[JValue]]
,那么链接仍将按顺序处理,但是单独的线程,不会给我任何效率,因此我需要得到List[JValue]
。
尝试使用ParSeq
进行计算在这个选项中,我有一个只提取数据的函数:
def extractData(link: String): JValue = // some implementation
但这次在集合上调用.par
:
val res: ParSeq[JValue] = listOfLinks.map(extractData)
但是通过这种方式,我不太明白如何在不按顺序解析每个链接的情况下阻止主线程,而不会按顺序解析每个链接
至于Akka,我不能在这里使用演员,所以只有Future
或Par*
答案 0 :(得分:5)
当您在集合上映射extractData
时,将并行处理链接。考虑一个略微简化的例子:
import scala.concurrent._
import ExecutionContext.Implicits.global
def extractData(s: String) = future {
printf("Starting: %s\n", s)
val i = s.toInt
printf("Done: %s\n", s)
i
}
val xs = (0 to 5).map(_.toString).toList
val parsed = Future.sequence(xs map extractData)
现在你会看到类似下面的内容,这清楚地说明这些事情并没有按顺序处理:
Starting: 0
Done: 0
Starting: 2
Done: 2
Starting: 1
Starting: 4
Done: 1
Starting: 3
Starting: 5
Done: 5
Done: 4
Done: 3
请注意,您可以使用Future.traverse
来避免创建期货的中间列表:
val parsed = Future.traverse(xs)(extractData)
在任何一种情况下,您都可以屏蔽Await
:
val res = Await.result(parsed, duration.Duration.Inf)
作为脚注:我不知道您是否计划使用Dispatch来执行HTTP请求,但如果没有,则值得一看。它还提供了很好的集成JSON解析,文档中充满了如何使用future的有用示例。