68

いくつかの 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 _) はメイン スレッドで実行されますか?そうでない場合、それを確認するにはどうすればよいですか?

アドバイスありがとうございます!

4

3 に答える 3

135

Scala 2.10 で Future を使用します。これらは、Scala チーム、Akka チーム、および Twitter 間の共同作業であり、フレームワーク全体で使用するためのより標準化された将来の API と実装に到達しました。ガイドを公開しました: http://docs.scala-lang.org/overviews/core/futures.html

Scala 2.10 の Future には、完全にノンブロッキング (デフォルトではマネージド ブロッキング操作を実行する機能が用意されています) であり、構成可能であるだけでなく、タスクを実行するための暗黙的なスレッド プールと、タイムアウトを管理するためのユーティリティが付属しています。

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

imagesFutsあなたが最初に行ったものとほとんど同じままです-ここでの唯一の違いは、マネージドブロッキングを使用することです- blocking. 渡されたコードのブロックに実行時間の長い操作またはブロックしている操作が含まれていることをスレッド プールに通知します。これにより、プールは新しいワーカーを一時的に生成して、すべてのワーカーがブロックされることが決して起こらないようにすることができます。これは、ブロッキング アプリケーションでの枯渇 (スレッド プールのロックアップ) を防ぐために行われます。スレッド プールは、マネージド ブロッキング ブロック内のコードがいつ完了するかを認識しているため、その時点で予備のワーカー スレッドを削除します。つまり、プールは予想されるサイズに縮小されます。

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

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

Awaitオブジェクトはdisplay、呼び出し元のスレッドで実行されることを保証する方法です。Await.resultつまり、渡されたフューチャーが完了するまで、現在のスレッドを強制的に待機させるだけです。(これは内部的にマネージド ブロッキングを使用します。)

于 2012-10-27T11:04:23.713 に答える
5
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 によって待機する未来が提供されます。
于 2012-10-27T08:39:57.813 に答える
3
  1. はい、私には問題ないように思えますが、より強力なtwitter-utilまたはAkka Future API を調査することをお勧めします (Scala 2.10 には、このスタイルの新しい Future ライブラリがあります)。

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

  3. いいえ、そうはなりません。これには、GUI ツールキットの標準メカニズムを使用する必要があります ( SwingUtilities.invokeLaterSwing またはDisplay.asyncExecSWT 用)。例えば

    fimages.foreach (_.foreach(im => SwingUtilities.invokeLater(new Runnable { display im })))
    
于 2012-10-27T06:57:26.303 に答える