Scala并行文件下载

Scala并行文件下载,scala,parallel-processing,future,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 => ...) 我看到的一个问题是,现在我只能在下载完所有文件后才能访

假设我有一个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 => ...)
我看到的一个问题是,现在我只能在下载完所有文件后才能访问这些文件。如何做到简洁、优雅,并能在下载文件时对其进行操作?

如何:

urls.par.map(fetch).map(file => ...)
通过这种方式,回迁和“处理”回迁文件是并行的。

如何:

urls.par.map(fetch).map(file => ...)
这样,获取和“处理”获取的文件都是并行完成的

…以更好的方式并行下载这些文件

这取决于“并行下载这些文件”的确切含义。假设您要下载三个文件(基于您的示例:
valurls=List(url1、url2、url3)
)。这可能意味着两件不同的事情:

  • 只需将下载从当前线程移开,而不关心下载本身是并行执行还是顺序执行(因此可能在url2之前获取url1,然后获取url3),
  • 将下载从当前线程移开,同时并行执行下载(同时获取所有URL)
如果第一个选项是您想要的,那么Tzach Zohar提供的答案是一个很好的方法。并行集合将把URL放在分区中,并为每个分区分配一个线程。如果您有3个元素,您的下载很可能会按顺序进行,因为只有1个分区。如果您的
URL列表
更大,那么您也将获得更多的线程,但是每个分区内的URL仍将按顺序获取

如果您希望同时下载所有文件(选项2),那么您需要对并行性进行更多的控制。您的
Future
方法并没有那么错误,但是您需要为每个url提供一个
Future
,而不是将整个下载过程放在一个
Future

您的代码可能如下所示:

val futureFiles: List[Future[File]] = urls.map(u => Future(fetch(u))) // note: no par
请注意,您现在得到的是一个
列表[未来[文件]]
,而不是像以前那样的
未来[列表[文件]]
。随后,您可以单独映射每个
未来
,而不必像以前一样等待一个
未来
完成

或者,您可以使用
Future.sequence
将生成的
List[Future[T]]
转换为
Future[List[T]

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

…以更好的方式并行下载这些文件

这取决于“并行下载这些文件”的确切含义。假设您要下载三个文件(基于您的示例:
valurls=List(url1、url2、url3)
)。这可能意味着两件不同的事情:

  • 只需将下载从当前线程移开,而不关心下载本身是并行执行还是顺序执行(因此可能在url2之前获取url1,然后获取url3),
  • 将下载从当前线程移开,同时并行执行下载(同时获取所有URL)
如果第一个选项是您想要的,那么Tzach Zohar提供的答案是一个很好的方法。并行集合将把URL放在分区中,并为每个分区分配一个线程。如果您有3个元素,您的下载很可能会按顺序进行,因为只有1个分区。如果您的
URL列表
更大,那么您也将获得更多的线程,但是每个分区内的URL仍将按顺序获取

如果您希望同时下载所有文件(选项2),那么您需要对并行性进行更多的控制。您的
Future
方法并没有那么错误,但是您需要为每个url提供一个
Future
,而不是将整个下载过程放在一个
Future

您的代码可能如下所示:

val futureFiles: List[Future[File]] = urls.map(u => Future(fetch(u))) // note: no par
请注意,您现在得到的是一个
列表[未来[文件]]
,而不是像以前那样的
未来[列表[文件]]
。随后,您可以单独映射每个
未来
,而不必像以前一样等待一个
未来
完成

或者,您可以使用
Future.sequence
将生成的
List[Future[T]]
转换为
Future[List[T]


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

此外,您不能在
未来的
中包装一些计算,并期望它是异步/非阻塞的(请参阅注释)。如果是那么简单我会多么喜欢。。。XD您最好停止使用,或者使用其他真正异步的HTTP库。@疯狂如果有一个正确配置的执行上下文,下载仍然会并行进行,这就是他所要求的,还是我错了?你是对的,你不可能在将来通过包装使阻塞代码成为非阻塞代码,但这正是我们目前使用的大多数SQL驱动程序的情况。是的,+1表示播放WS:)@fxlae是的,它将并行执行。最终结果是一样的。:)唯一的区别是(我认为)运行这些HTTP请求的线程将被阻塞(等待,不做任何有用的事情),直到响应返回。另外,您不能在
未来
中包装一些计算,并期望它是简单的