Scala中的asynchronousIO与期货
比方说,我正在从某些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个图像下载,这将创build100个线程一次,或将使用线程池?
- 最后一条指令(
display _
)是否会在主线程上执行,如果没有,我怎么确定它?
谢谢你的build议!
在Scala 2.10中使用期货。 他们是Scala团队,Akka团队和Twitter之间的联合工作,以达到更为标准化的未来API和实现,以便跨框架使用。 我们只是在http://docs.scala-lang.org/overviews/core/futures.html发表了一个指南;
除了完全无阻塞(默认情况下,尽pipe我们提供了执行pipe理阻塞操作的能力)和可组合的,Scala的2.10版本还带有隐式线程池来执行您的任务,还有一些实用程序来pipe理超时。
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
:pipe理阻止的API。 -
Future
:未来伴侣对象,其中包含一些有用的方法收集期货。 -
Await
:用于阻塞未来的单体对象(将其结果传输到当前线程)。 -
ExecutionContext.Implicits.global
:默认的全局线程池,一个ForkJoin池。 -
duration._
:用于pipe理超时duration._
实用程序。
imagesFuts
基本上与您原来imagesFuts
基本相同 – 这里唯一的区别是我们使用受pipe理的阻止blocking
。 它会通知线程池您传递给它的代码块包含长时间运行或阻塞操作。 这样可以让游泳池临时产生新的工人,以确保所有的工人都不会被堵塞。 这样做是为了防止阻塞应用程序中的饥饿(locking线程池)。 请注意,线程池还知道何时pipe理的阻塞块中的代码已完成,因此它将在此时删除备用工作线程,这意味着该池将缩小到预期的大小。
(如果你想完全防止创build额外的线程,那么你应该使用一个AsyncIO库,比如Java的NIO库。)
然后,我们使用Future companion对象的集合方法将List[Future[...]]
中的imagesFuts
转换为Future[List[...]]
。
Await
对象是我们如何确保在调用线程上执行display
Await.result
只是强制当前线程等待,直到它传递的未来完成。 (这在内部使用受pipe理的阻止。)
val all = Future.traverse(urls){ url => val f = future(download url) /*(downloadContext)*/ f.onComplete(display)(displayContext) f } Await.result(all, ...)
- 在2.10中使用scala.concurrent.Future,现在是RC。
- 它使用一个隐含的ExecutionContext
- 新的Future文档明确指出,如果该值可用,onComplete(和foreach)可以立即评估。 老演员未来也做同样的事情。 根据您的要求显示,您可以提供一个合适的ExecutionContext(例如,一个线程执行程序)。 如果你只是想让主线程等待加载完成,遍历会给你一个未来的等待。
-
是的,对我来说似乎很好,但是你可能想要研究更强大的twitter-util或者Akka Future API(Scala 2.10将会有一个新的Future库)。
-
它使用一个线程池。
-
不,不会的 您需要使用GUI工具包的标准机制(
SwingUtilities.invokeLater
或SWT的Display.asyncExec
)。 例如fimages.foreach (_.foreach(im => SwingUtilities.invokeLater(new Runnable { display im })))