私はしばらくScala実行コンテキスト、基礎となるスレッドモデル、および同時実行性のトピックを学びました。scala.concurrent.blocking
"ランタイム動作を調整する"および "パフォーマンスを向上させるか、デッドロックを回避する"scaladoc ?
ドキュメント では、Awaitableを実装しないAPIを待機する手段として示されています。 (おそらく、実行時間の長い計算もラップする必要がありますか?)。
それは実際に何をしているのですか?
ソースの追跡 は、その秘密を簡単に裏切ることはできません。
blocking
は、含まれているコードがブロックしており、スレッドが不足する可能性があるというExecutionContext
へのヒントとして機能することを目的としています。これにより、スレッドプールに、枯渇を防ぐために新しいスレッドを生成する機会が与えられます。これは、「ランタイム動作を調整する」が意味するものです。しかし、それは魔法ではなく、すべてのExecutionContext
で機能するわけではありません。
この例を考えてみましょう:
import scala.concurrent._
val ec = scala.concurrent.ExecutionContext.Implicits.global
(0 to 100) foreach { n =>
Future {
println("starting Future: " + n)
blocking { Thread.sleep(3000) }
println("ending Future: " + n)
}(ec)
}
これはデフォルトのグローバルExecutionContext
を使用しています。コードをそのまま実行すると、100個のFuture
sがすべてすぐに実行されますが、blocking
を削除すると、一度に数個しか実行されません。デフォルトのExecutionContext
は、新しいスレッドを生成することで(そのようにマークされた)ブロッキング呼び出しに反応するため、実行中のFuture
sでオーバーロードされません。
次に、4つのスレッドの固定プールでこの例を見てください。
import Java.util.concurrent.Executors
val executorService = Executors.newFixedThreadPool(4)
val ec = ExecutionContext.fromExecutorService(executorService)
(0 to 100) foreach { n =>
Future {
println("starting Future: " + n)
blocking { Thread.sleep(3000) }
println("ending Future: " + n)
}(ec)
}
このExecutionContext
は、新しいスレッドの生成を処理するように構築されていないため、ブロックコードがblocking
で囲まれている場合でも、一度に最大4つのFuture
sしか実行されないことがわかります。そして、だからこそ私たちはそれを言う「パフォーマンスを向上させるか、デッドロックを回避するかもしれない」-それは保証されていません。後者のExecutionContext
を見るとわかるように、これはまったく保証されていません。
それはどのように機能しますか?リンクされているとおり、blocking
は次のコードを実行します。
BlockContext.current.blockOn(body)(scala.concurrent.AwaitPermission)
BlockContext.current
は、現在のスレッドからBlockContext
を取得します here を参照してください。 BlockContext
は通常、Thread
特性が混在したBlockContext
です。ソースに示されているように、ThreadLocal
に格納されているか、またはそこに見つからない場合は、現在のスレッドからパターンマッチングされます。現在のスレッドがBlockContext
でない場合は、代わりにDefaultBlockContext
が使用されます。
次に、blockOn
が現在のBlockContext
で呼び出されます。 blockOn
はBlockContext
の抽象メソッドであるため、実装はExecutionContext
による処理方法に依存します。 DefaultBlockContext
の実装 (現在のスレッドがBlockContext
ではない場合)を見ると、blockOn
は実際には何もしていません。したがって、blocking
以外でBlockContext
を使用するということは、特別なことは何も行われず、コードは現状のまま実行され、副作用はありません。
BlockContext
sであるスレッドについてはどうですか?たとえば、global
コンテキストでは here を見て、blockOn
はかなり多くのことを行います。さらに掘り下げてみると、内部でForkJoinPool
を使用しており、DefaultThreadFactory
は、ForkJoinPool
で新しいスレッドを生成するために使用されている同じスニペットで定義されています。 blockOn
(スレッド)からBlockContext
を実装しないと、ForkJoinPool
はブロックしていることを認識せず、応答としてより多くのスレッドを生成しようとしません。
Scalaの Await
も、その実装にblocking
を使用します。