2013-03-02 11 views
12

私はちょうど不自然な動作に気づいた。 のは、私が唯一のオブジェクトからなるスタンドアロンのプログラムがあるとしましょう:Scala:オブジェクトイニシャライザの並列コレクションにより、プログラムがハングする

object ParCollectionInInitializerTest { 
    def doSomething { println("Doing something") } 

    for (i <- (1 to 2).par) { 
    println("Inside loop: " + i) 
    doSomething 
    } 

    def main(args: Array[String]) { 
    } 
} 

プログラムは完全に無実であると、forループで使用される範囲は、パラレル1ないときは、次の出力で、適切に実行されます:

ループ内

:1
こう何か
ループ内側:2
が何か

を行います並列コレクションを使用している場合

は残念ながら、プログラムは単に今までのdoSomethingメソッドを呼び出さずにハングアップするので、出力は次のとおりです。ループ内

:2
ループ内側:1

そして、プログラムがハングします。
これは単なる厄介なバグですか?私はscala-2.10を使用しています。

+0

関連:http:// stackoverflow。com/questions/27549671/java-static-initializersの診断やデッドロックのデッドロック – Rich

答えて

15

これは、構築が完了する前にシングルトンオブジェクトへの参照を解放するときにScalaで発生する可能性のある固有の問題です。オブジェクトが完全に構築される前に、別のスレッドがオブジェクトParCollectionInInitializerTestにアクセスしようとしているために起こります。 mainメソッドとは関係がありません。mainメソッドを含むオブジェクトを初期化する必要があります。これをREPLで実行し、式ParCollectionInInitializerTestを入力すると同じ結果が得られます。また、デーモンスレッドであるfork-joinワーカースレッドとは関係ありません。

シングルトンオブジェクトは遅延して初期化されます。すべてのシングルトンオブジェクトは、一度だけ初期化できます。つまり、オブジェクトにアクセスする最初のスレッド(あなたの場合はメインスレッド)がオブジェクトのロックを取得してから初期化する必要があります。その後に来る他のすべてのスレッドは、メインスレッドがオブジェクトを初期化し、最終的にロックを解放するのを待たなければなりません。これは、シングルトンオブジェクトがScalaで実装される方法です。

並列収集ワーカースレッドは、シングルトンオブジェクトにアクセスしてdoSomethingを呼び出そうとしますが、メインスレッドがオブジェクトの初期化を完了するまで待つことができません。一方、メインスレッドは、すべてのワーカースレッドが完了すると条件付きの並列操作が完了するまでコンストラクタで待機します。メインスレッドは常にシングルトンの初期化ロックを保持します。したがって、デッドロックが発生します。

下図のようにあなたは、2.10から、あるいは単なるスレッドを先物で、この動作を引き起こす可能性があります:

def execute(body: =>Unit) { 
    val t = new Thread() { 
    override def run() { 
     body 
    } 
    } 

    t.start() 
    t.join() 
} 

object ParCollection { 

    def doSomething() { println("Doing something") } 

    execute { 
    doSomething() 
    } 

} 

はREPLにこれを貼り付け、書き込み:

scala> ParCollection 

とREPLハングアップします。

+2

優れた説明、ありがとう! –

+1

同時ブロッキングの実行と遅延初期化は、うまく一緒に実行されません。これはScala(とJava、その点で)のより一般的な問題です。このSIPを参照してください:http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html – axel22

+0

レトルトとしては意味しませんが、これは開発者にとって落とし穴だと思います。私は最初の答えとコメントに非常に感謝しています。私はその答えが途中で私にはっきりしていたと信じています。 – matanster

関連する問題