假设我有一个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 => ...)我看到的一个问题是,现在我只能在所有文件都下载后才能访问它们。如何做到如此简洁和优雅,并且能够在下载的每个文件上执行操作?
发布于 2016-10-19 16:11:04
这样如何:
urls.par.map(fetch).map(file => ...)以这种方式,获取和“处理”所获取的文件是并行完成的。
发布于 2016-10-19 18:32:13
...a并行下载这些文件的更好方法...
这取决于你所说的“并行下载这些文件”到底是什么意思。假设您想要下载三个文件(基于示例:val urls = List(url1, url2, url3))。这可能意味着两件不同的事情:
如果第一个选项是您想要的,那么Tzach Zohar提供的答案是一个很好的方法。并行收集将把您的urls放在分区中,并为每个分区分配一个线程。如果你有3个元素,你的下载很可能是按顺序进行的,因为只有一个分区。如果您的urls的List更大,那么您也将获得更多的线程,但每个分区中的urls仍将按顺序获取。
如果你想同时下载所有文件(选项2),那么你需要对并行性进行更多的控制。您的Future方法并没有那么错误,但是您需要为每个Future提供一个Future,而不是将整个下载过程放在一个url中。
您的代码可能如下所示:
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 => ...))之后,您可以选择使用Future.sequence将生成的List[Future[T]]转换为Future[List[T]]。
您必须确保使用正确配置的ExecutionContext,否则,您的部分下载可能仍会按顺序执行。除此之外,用一些真正异步的东西替换你的阻塞IO是个好主意(参见insan-e的评论)。
https://stackoverflow.com/questions/40125555
复制相似问题