IT-Swarm.Net

异步 IO 在斯卡拉与期货

假设我从一些URL下载了一个(可能很大的)图像列表。我正在使用Scala,所以我要做的是:

import scala.actors.Futures._

// Retrieve URLs from somewhere
val urls: List[String] = ...

// Download image (blocking operation)
val fimages: List[Future[...]] = urls.map (url => future { download url })

// Do something (display) when complete
fimages.foreach (_.foreach (display _))

我对Scala有点新意,所以对我来说这看起来仍然有些神奇:

  • 这是正确的方法吗?任何替代品,如果不是?
  • 如果我要下载100个图像,这会一次创建100个线程,还是会使用线程池?
  • 最后一条指令(display _)是否会在主线程上执行,如果没有,我该如何确定它?

谢谢你的建议!

67
F.X.

在Scala 2.10中使用Futures。他们是Scala团队,Akka团队和Twitter之间的联合工作,以实现更加标准化的未来API和跨框架使用的实现。我们刚刚发布了一个指南: http://docs.scala-lang.org/overviews/core/futures.html

除了完全非阻塞(默认情况下,尽管我们提供了管理阻塞操作的能力)和可组合之外,Scala的2.10期货还带有一个隐式线程池来执行您的任务,以及一些管理超时的实用程序。.

import scala.concurrent.{future, blocking, Future, Await, ExecutionContext.Implicits.global}
import scala.concurrent.duration._

// Retrieve URLs from somewhere
val urls: List[String] = ...

// Download image (blocking operation)
val imagesFuts: List[Future[...]] = urls.map {
  url => future { blocking { download url } }
}

// Do something (display) when complete
val futImages: Future[List[...]] = Future.sequence(imagesFuts)
Await.result(futImages, 10 seconds).foreach(display)

上面,我们首先导入一些东西:

  • future:用于创建未来的API。.
  • blocking:用于托管阻止的API。.
  • Future:未来的伴随对象,包含许多有用的方法 collections future。.
  • Await:用于阻止未来的单一对象(将其结果传输到当前线程)。.
  • ExecutionContext.Implicits.global:默认的全局线程池,一个ForkJoin池。.
  • duration._:用于管理超时持续时间的实用程序。.

imagesFuts与你最初做的大致相同 - 唯一的区别是我们使用托管阻塞blocking。它通知线程池您传递给它的代码块包含长时间运行或阻塞操作。这允许池临时生成新工作程序,以确保永远不会发生所有工作程序被阻止的情况。这样做是为了防止阻塞应用程序中的饥饿(锁定线程池)。请注意,线程池还知道托管阻塞块中的代码何时完成 - 因此它将在该点删除备用工作线程,这意味着池将缩减回其预期大小。.

(如果您想绝对阻止创建其他线程,那么您应该使用AsyncIO库,例如Java的NIO库。)

然后我们使用Future伴随对象的集合方法将imagesFutsList[Future[...]]转换为Future[List[...]]。.

Await对象是我们如何确保在调用线程上执行display的方法 - Await.result只是强制当前线程等到它传递的未来完成。 (这在内部使用托管阻止。)

128
Heather Miller

快速,可靠且价格合理的云托管

注册并在30天内获得$50奖金!
val all = Future.traverse(urls){ url =>
  val f = future(download url) /*(downloadContext)*/
  f.onComplete(display)(displayContext)
  f
}
Await.result(all, ...)
  1. 在2.10中使用scala.concurrent.Future,现在是RC。.
  2. 它使用隐式ExecutionContext
  3. 新的Future doc明确指出onComplete(和foreach)可以在值可用时立即进行评估。老演员Future做同样的事情。根据您的要求显示,您可以提供合适的ExecutionContext(例如,单个线程执行程序)。如果您只是希望主线程等待加载完成,则遍历会为您提供等待的未来。.
5
som-snytt
  1. 是的,对我来说似乎很好,但你可能想调查更强大的 Twitter-utilAkka Future API(Scala 2.10将有这种风格的新Future库)。.

  2. 它使用线程池。.

  3. 不,它不会。您需要使用GUI工具包的标准机制(Swing为SwingUtilities.invokeLater或SWT为Display.asyncExec)。例如。.

    fimages.foreach (_.foreach(im => SwingUtilities.invokeLater(new Runnable { display im })))
    
3
Alexey Romanov