web-dev-qa-db-ja.com

非同期IO in Scala with futures

いくつかの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これらは、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。
  • Futurefuturesのコレクションに役立つメソッドを多数含むFutureコンパニオンオブジェクト。
  • Await:将来のブロック(結果を現在のスレッドに転送)に使用されるシングルトンオブジェクト。
  • ExecutionContext.Implicits.global:デフォルトのグローバルスレッドプールであるForkJoinプール。
  • duration._:タイムアウトの期間を管理するためのユーティリティ。

imagesFutsは元の状態とほぼ同じままです。ここでの唯一の違いは、マネージドブロッキングを使用することですblocking。スレッドプールに、渡したコードブロックに長時間実行またはブロック操作が含まれていることを通知します。これにより、プールは一時的に新しいワーカーを生成し、すべてのワーカーがブロックされないようにします。これは、ブロッキングアプリケーションでの飢star(スレッドプールのロックアップ)を防ぐために行われます。スレッドプールは、マネージドブロックブロックのコードがいつ完了するかを知っているため、その時点でスペアワーカースレッドを削除することに注意してください。つまり、プールは予想サイズまで縮小します。

(追加のスレッドが作成されないようにする場合は、JavaのNIOライブラリなどのAsyncIOライブラリを使用する必要があります。)

次に、Futureコンパニオンオブジェクトのコレクションメソッドを使用して、imagesFutsList[Future[...]]からFuture[List[...]]に変換します。

Awaitオブジェクトは、displayが呼び出しスレッドで実行されることを確認する方法です。Await.resultは、渡された未来が完了するまで現在のスレッドを単に待機させるだけです。 (これは内部で管理されたブロッキングを使用します。)

129
Heather Miller
val all = Future.traverse(urls){ url =>
  val f = future(download url) /*(downloadContext)*/
  f.onComplete(display)(displayContext)
  f
}
Await.result(all, ...)
  1. 現在RCである2.10でscala.concurrent.Futureを使用します。
  2. 暗黙のExecutionContextを使用します
  3. 新しいFuture docは、値が利用可能であればonComplete(およびforeach)がすぐに評価できることを明示しています。古い俳優のフューチャーも同じことをしています。表示の要件に応じて、適切なExecutionContext(たとえば、シングルスレッドエグゼキューター)を指定できます。メインスレッドがロードの完了を待機するだけの場合、traverseは待機する未来を提供し​​ます。
5
som-snytt
  1. はい、私には問題ないようですが、より強力な Twitter-util または Akka Future APIを調査することをお勧めします(Scala 2.10にはこのスタイルの新しいFutureライブラリがあります)。

  2. スレッドプールを使用します。

  3. いいえ、できません。このためには、GUIツールキットの標準メカニズムを使用する必要があります(SwingUtilities.invokeLaterスイングまたはDisplay.asyncExec(SWTの場合)。例えば。

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