Scala并行文件下载

时间:2016-10-19 08:05:24

标签: scala parallel-processing future

想象一下,我有一个URL列表和一个下载相应文件的函数:

val urls = List(url1, url2, url3)
def fetch(url: String): File = ...

我想知道是否有更好的方法来并行下载这些文件:

val futureFiles: Future[List[File]] = Future {
  urls.par.map(fetch)
}
futureFiles.map(files => ...)

我看到的一个问题是,现在我只能在下载文件后才能访问这些文件。如何实现简洁优雅的东西,并能够在我们去的时候对每个下载的文件采取行动?

2 个答案:

答案 0 :(得分:2)

怎么样:

urls.par.map(fetch).map(file => ...)

这样,获取文件的提取和“处理”都是并行完成的。

答案 1 :(得分:0)

  

...更好的并行下载这些文件的方法......

这取决于你的意思是“并行下载这些文件”。想象一下,您想要下载三个文件(基于您的示例:val urls = List(url1, url2, url3))。这可能意味着两件事:

  • 只是将下载从当前线程移开,但不关心下载本身是并行执行还是顺序执行(因此可能在url2之前获取url1,然后是url3),
  • 将下载文件从当前线程移开,并同时自行执行下载(同时获取所有网址)。

如果第一个选项是您想要的,那么Tzach Zohar提供的答案是一个很好的方法。并行集合将把您的URL放在分区中,并为每个分区分配一个线程。如果你有3个元素,你的下载很可能会顺序完成,因为只有1个分区。如果您的List网址较大,那么您还会获得更多线程,但每个分区中的网址仍将按顺序获取。

如果您想要同时下载所有文件(选项2),那么您需要更多地控制并行性。您的Future方法没有错,但不是将整个下载过程放在一个Future中,而是每个网址需要一个Future

您的代码可能如下所示:

val futureFiles: List[Future[File]] = urls.map(u => Future(fetch(u))) // note: no par

请注意,您现在获得List[Future[File]],而不是之前的Future[List[File]]。随后,您可以单独映射每个Future,而不必等待一个Future完成(如前所述)。

futureFiles.map(_.map(file => ...))

您可以选择使用List[Future[T]]将结果Future[List[T]]转换为Future.sequence

您必须确保使用正确配置的ExecutionContext,否则,部分下载内容仍可按顺序执行。除此之外,用真正异步的东西替换阻塞IO是个好主意(参见insan-e的评论)。