2014-01-17 10 views
10

This answerの新しいスレッドは、scala.concurrent.Futurejava.util.concurrent.Futureを変換する方法を指示する理由:ブロッキングが発生する場所を管理しながら、代わりに将来{...}

import java.util.concurrent.{Future => JFuture} 
import scala.concurrent.{Future => SFuture} 

val jfuture: JFuture[T] = ??? 
val promise = Promise[T]() 
new Thread(
    new Runnable { 
    def run() { promise.complete(Try{ jfuture.get }) } 
    } 
).start 
val future = promise.future 

マイquestonはコメントで聞かれる質問と同じです:

future { jfuture.get }に間違っていますか? Promiseと組み合わせた余分なスレッドを使用した理由

これは次のように答えた:

それはあなたのスレッドプルでスレッドをブロックします。このような未来のために設定されたExecutionContextを持っているなら、それは問題ありませんが、デフォルトのExecutionContextには、あなたが持っているプロセッサーと同じ数のスレッドが含まれています。

私は説明を理解していますか分かりません。繰り返す:

future { jfuture.get }の何か問題がありますか?未来をブロックすることは、手動で新しいスレッドを作成してブロックすることと同じですか?そうでない場合、どう違うのですか?

+0

あなたは正確に何を理解していませんか?スレッドのブロックは何を意味しますか? –

+0

@AlexeiKaigorodov私は多少私の質問を改訂しました: 'future {jfuture.get} 'の何が間違っていますか?未来をブロックすることは、手動で新しいスレッドを作成してブロックすることと同じですか?そうでない場合、どう違うのですか? –

+0

はい、未来の中でブロックするのは、手動で新しいスレッドを作成してそこでブロックするというAS BADです。 'scala.concurrent.Future'に変換するという考えは、' get'の代わりに 'onComplete'を使って完全にブロックするのを避けることです。 –

答えて

8

future { jfuture.get }future { future { jfuture.get }}の間にはほとんど違いがありません。

既定のスレッドプールには、プロセッサを持つスレッドと同数のスレッドがあります。

jfuture.getとすると、1つのスレッドがブロックされます。

プロセッサーが8台あるとします。また、それぞれjfuture.getが10秒かかるとします。今すぐ8 future { jfuture.get }を作成します。

val format = new java.text.SimpleDateFormat("HH:mm:ss").format(_: Date) 

val startTime = new Date 
(1 to 8) map {_ => future{ Thread.sleep(10000) }} 
future{ 
    2+2 
    println(s"2+2 done. Start time: ${format(startTime)}, end time: ${format(new Date)}") 
} 

// 2+2 done. Start time: 20:48:18, end time: 20:48:28 

評価が2+2の場合、10秒が少し長すぎます。

他のすべてfutureと、同じ実行コンテキストのすべてのアクタが10秒間停止します。

追加の実行コンテキストに

object BlockingExecution { 
    val executor = ExecutionContext.fromExecutor(new ForkJoinPool(20)) 
} 

def blockingFuture[T](f: => T) = { 
    future(f)(BlockingExecution.executor) 
} 

val startTime = new Date 
(1 to 8) map {_ => blockingFuture{ Thread.sleep(10000) }} 
future{ 
    2+2 
    println(s"2+2 done. Start time: ${format(startTime)}, end time: ${format(new Date)}") 
} 

// 2+2 done. Start time: 21:26:18, end time: 21:26:18 

あなたはnew Thread(new Runnable {...を使用してblockingFutureを実装することができますが、追加の実行コンテキストを使用すると、スレッドがカウント制限することができます。

+0

2 + 2を計算する未来が他の先物の後に実行される必要があるという固有の理由はありません。 (Future @ Thread.sleep(10000)}を 'Future {blocking {Thread.sleep(10000)}} 'に変更すると、2 + 2の将来は自動的に非同期に計算されます。すぐに実行します。スレッドプールが他の先物を処理するために、 'blocking'メソッドがヒントを与えることは私の理解である。私はここで何が起こっているかを正確に説明する良いリソースは見つかりませんでした。 – Patrick

+0

@Patrickそれは本当です。 'blocking'を使うとき、グローバルスレッドプールはそのスレッドがスレッドを使い果たしないようにその計算のための新しいスレッドを生成します。詳細はこちら:http://docs.scala-lang.org/overviews/core/futures.html「ブロッキング」を参照してください。 – simao

7

実際は非常に簡単です。 scala.concurrent.Promiseは、Futureの具体的な実装であり、非同期計算であることが予定されています。

jfuture.getを使用して変換する場合は、ブロック計算を実行してすぐに解決されたscala.concurrent.Futureを出力しています。

Threadは、jfutureの計算が完了するまでブロックされます。 getメソッドがブロックされています。

ブロックすることは、計算が完了するまでその中で何も起こらないことを意味します。Threadあなたは本質的にThreadを独占していて、whileのループが間欠的に結果を確認するようなもので独占しています。

具体
while (!isDone() && !timeout) { 
    // check if the computation is complete 
} 

:ブロックを避けることができない場合は

val jfuture: JFuture[T] = ??? // some blocking task 

、一般的な方法は、子スレッドを独占/計算を実行できるようにするためにnew Threadnew Runnableまたはnew Callableを起動することです。例@seniaで

は与えた:

new Thread(new Runnable { def run() { 
    promise.complete(Try{ jfuture.get }) 
}}).start 

方法future {jfuture.get}よりも、この違うのですか? Scalaによって提供されるデフォルトのExecutionContextをブロックするものではありません。Scalaでは、マシンのプロセッサーと同じ数のスレッドがあります。

これは、コンテキスト全体がブロックされているため、コード内の他のすべての未来が常にfuture { jfuture.get }が完了するまで待つ必要があることを意味します。

+0

私は、新しいスレッドを生成し、その中で 'somethingThatBlocks()'を実行することは将来(somethingThatBlocks())と同じであるという印象を受けています。私の質問は、それが本当であるかどうかについての大部分ですが、そうでない場合はどう違うのですか? –

+0

だから、一般的には、デフォルトの実行コンテキストで未来の中で長い計算をすることはお勧めしませんか? –

+1

@DominykasMostauskis:いいえ、デフォルトの実行コンテキストにはスレッドプールがあり、将来のコードを実行するためにスレッドプールの1つを選択します。スレッドの作成は高価な操作であり、スレッドは限られたリソースなので、毎回新しいスレッドにまたがることはありません。 –

関連する問題