web-dev-qa-db-ja.com

scala.concurrent.blocking-実際には何をしますか?

私はしばらくScala実行コンテキスト、基礎となるスレッドモデル、および同時実行性のトピックを学びました。scala.concurrent.blocking "ランタイム動作を調整する"および "パフォーマンスを向上させるか、デッドロックを回避する"scaladoc

ドキュメント では、Awaitableを実装しないAPIを待機する手段として示されています。 (おそらく、実行時間の長い計算もラップする必要がありますか?)。

それは実際に何をしているのですか?

ソースの追跡 は、その秘密を簡単に裏切ることはできません。

47
matanster

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個のFuturesがすべてすぐに実行されますが、blockingを削除すると、一度に数個しか実行されません。デフォルトのExecutionContextは、新しいスレッドを生成することで(そのようにマークされた)ブロッキング呼び出しに反応するため、実行中のFuturesでオーバーロードされません。

次に、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つのFuturesしか実行されないことがわかります。そして、だからこそ私たちはそれを言う「パフォーマンスを向上させるか、デッドロックを回避するかもしれない」-それは保証されていません。後者のExecutionContextを見るとわかるように、これはまったく保証されていません。

それはどのように機能しますか?リンクされているとおり、blockingは次のコードを実行します。

BlockContext.current.blockOn(body)(scala.concurrent.AwaitPermission)

BlockContext.currentは、現在のスレッドからBlockContextを取得します here を参照してください。 BlockContextは通常、Thread特性が混在したBlockContextです。ソースに示されているように、ThreadLocalに格納されているか、またはそこに見つからない場合は、現在のスレッドからパターンマッチングされます。現在のスレッドがBlockContextでない場合は、代わりにDefaultBlockContextが使用されます。

次に、blockOnが現在のBlockContextで呼び出されます。 blockOnBlockContextの抽象メソッドであるため、実装はExecutionContextによる処理方法に依存します。 DefaultBlockContextの実装 (現在のスレッドがBlockContextではない場合)を見ると、blockOnは実際には何もしていません。したがって、blocking以外でBlockContextを使用するということは、特別なことは何も行われず、コードは現状のまま実行され、副作用はありません。

BlockContextsであるスレッドについてはどうですか?たとえば、globalコンテキストでは here を見て、blockOnはかなり多くのことを行います。さらに掘り下げてみると、内部でForkJoinPoolを使用しており、DefaultThreadFactoryは、ForkJoinPoolで新しいスレッドを生成するために使用されている同じスニペットで定義されています。 blockOn(スレッド)からBlockContextを実装しないと、ForkJoinPoolはブロックしていることを認識せず、応答としてより多くのスレッドを生成しようとしません。

Scalaの Await も、その実装にblockingを使用します。

75
Michael Zajac