2009-08-21 20 views
16

以下のコードでは、20個のスレッドを作成し、それぞれにメッセージを出力し、スリープして別のメッセージを表示させます。私は私のメインスレッドでスレッドを開始し、同様にすべてのスレッドに参加します。私はすべてのスレッドが終了した後にのみ "all done"メッセージが出力されることを期待します。しかし、すべてのスレッドが完了する前に、「すべて完了」が印刷されます。誰かが私にこの行動を理解するのを助けることができますか?スケーラで期待通りにスレッドが動作しない

ありがとうございました。 ケント

ここでは、コードは次のとおりです。ここで

def ttest() = { 
    val threads = 
     for (i <- 1 to 5) 
     yield new Thread() { 
      override def run() { 
      println("going to sleep") 
      Thread.sleep(1000) 
      println("awake now") 
      } 
     } 

    threads.foreach(t => t.start()) 
    threads.foreach(t => t.join()) 
    println("all done") 
    } 

が出力されます:あなたのコードで

going to sleep 
all done 
going to sleep 
going to sleep 
going to sleep 
going to sleep 
awake now 
awake now 
awake now 
awake now 
awake now 
+0

for-comprehensions、ranges、threadの特定のやりとりが共通のエラーパターンであるように思われるので、私はこの疑問を浮き彫りにしています。ありがとう、パヴェ。 –

答えて

7

threadsが延期された - あなたはそれを繰り返すたびに、forジェネレータ式が実行されます新しい。つまり、最初にforeachが5を作成して起動し、2番目のforeachが5つ(起動していない)を作成して結合します。実行されていないので、すぐにjoinが戻ります。安定したスナップショットを作成するには、forの結果にtoListを使用する必要があります。

+0

。それはまさにそれでした。 私は上記の本当に答えを削除する方法を知りません。 – Kent

11

あなたはListRangeを変換する場合、それは動作します:

def ttest() = { 
    val threads = 
     for (i <- 1 to 5 toList) 
     yield new Thread() { 
      override def run() { 
      println("going to sleep") 
      Thread.sleep(1000) 
      println("awake now") 
      } 
     } 

    threads.foreach(t => t.start()) 
    threads.foreach(t => t.join()) 
    println("all done") 
    } 

問題は「1 to 5」はRangeで、範囲はいわば、「厳格な」ではないということです。良い英語では、方法mapRangeに呼び出すと、すぐに各値が計算されません。代わりに、Scala 2.7のRandomAccessSeq.Projectionオブジェクトを生成します。この関数は、マップに渡された関数と元の範囲に渡された関数への参照を持ちます。したがって、結果の範囲の要素を使用すると、マップに渡された関数が元の範囲の対応する要素に適用されます。これは結果の範囲の要素にアクセスするたびに毎回発生します。

これは、tの要素を参照するたびに、new Thread() { ... }を新たに呼び出すことを意味します。 2回実行し、範囲に5つの要素があるので、10個のスレッドを作成しています。あなたは、これが混乱している場合、下記の例を見て最初の5で開始し、第二5

に参加:

scala> object test { 
    | val t = for (i <- 1 to 5) yield { println("Called again! "+i); i } 
    | } 
defined module test 

scala> test.t 
Called again! 1 
Called again! 2 
Called again! 3 
Called again! 4 
Called again! 5 
res4: scala.collection.generic.VectorView[Int,Vector[_]] = RangeM(1, 2, 3, 4, 5) 

scala> test.t 
Called again! 1 
Called again! 2 
Called again! 3 
Called again! 4 
Called again! 5 
res5: scala.collection.generic.VectorView[Int,Vector[_]] = RangeM(1, 2, 3, 4, 5) 

私はScalaのREPL印刷res4res5を持つことによって(tを印刷するたびに)、得られた式は再び評価されます。それはあまりにも個々の要素のために起こる:Scalaの2.8のよう

scala> test.t(1) 
Called again! 2 
res6: Int = 2 

scala> test.t(1) 
Called again! 2 
res7: Int = 2 

EDIT

を、Rangeは厳密になりますので、問題のコードは、当初の予想通りに動作します。

関連する問題