Scala Actor の reply が正しくできない問題は scala.actors.Channel が悪いのかも?
突っ込み大歓迎です。というか助けてください。
環境は、Mac OS 10.6.5, Scala 2.8.1.final, Java 1.6.0_22 となります。
検証用のコードは、Scala STM を試してみる その3 で使ったコードの修正版のこちらとなります。
STM と対比するために Actor のみを用いてカウンタのデモを書いたのですが、300 メッセージを超えた辺りから reply が失敗する状態となってしまいました。そこで reply 時に使われている Channel リストの操作に疑いを持ち、試しに返信用の Channel を作成し、それをメッセージで渡してみました。結果は、残念な事に 300 メッセージを超えた辺りから、Channel に送ったメッセージが取得できなくなりました。*1
最終的に、mailboxSize を確認すると良いのではないか?と考え、試しに下記を試してみたのですが…
object CounterInActor extends Actor { // ..snip.. def increment(): Int = { if (n < this.mailboxSize) { 1 } else { this !? Increment match { case n: Int => n case _ => 0 } } } // ..snip..
n の値を 0 にすると正しく動作しましたw;*2 300 近い Actor が、ほぼ同時にメッセージを送るので、確認した瞬間は 0 or 1 ですが、送り終わった後には 300 近辺の値になっていると思われます。そこで下記のように書き換えてみると…
object CounterInActor extends Actor { // ..snip.. def act() = loop { react { case Increment => println(mailboxSize) counter += 1 reply(counter) // ..snip.. } } // ..snip.. }
--<400>-- 0 310 309 308 307 306 305 304 303 302 301 300 299 298 385 384 383 382 381 380 379 378 377 376 375 374 373 372 371 370 369 368 367 366 365 364 363 362 361 360 359 358 357 356 // ..snip..
0 から 310 に跳ね上がってました。こうなって来ると、下記の workers を 200 個づつに分割して、間に Sleep を 50ms 入れる等の工夫が必要となります。
def test (testCases: (String, Supervisor)*) { // ..snip.. val results = workers map { _.increment() } map { future => future() } // ..snip.. }
大元の検証コードに誤りがあると嬉しいのですが…。今の所、reply が必要な場合は、Client となる Actor の並列度に制限を行うか STM の使用を検討する事しか、私には思いつきませんでした。
うーん…、Channel の処理を深追いするしか無いかな。