ちなみに

火曜日の空は僕を押しつぶした。

コンテキストスイッチがかからない

スレッドで詰まった点をメモ。

研究のプログラムで、dRubyとスレッドとプロセスが絡んでいるのを書いているのですが、あるプロセスで動いているdRubyサービス上のクラスから、別プロセス上にあるオブジェクのインスタンスメソッドを呼ぶようなスレッドを複数作ると、スレッドのコンテキストスイッチがかからずに(これも本当にそれが原因が分からないですが)、見かけ上直列に動いてしまって、スレッドを作る意味が無いという状況になりました。 1つ目のスレッドを作って、2つ目のスレッドを作るところまではいっているようなのですが、そこで最初のスレッドから他のスレッドにコンテキストスイッチしなくなってしまい、そのスレッド上での処理が終了するまで待ってしまっているようです。

5つのスレッドを作る場合

main:run, thread1:run, thread2:run
|
(a)mainではさらにスレッドを作る処理をしているはず
|
main:run, thread1:death, thread2:run, thread3:run
|
(a)
|
main:run, thread2:death, thread3:run, thread4:run
|
(a)
|
main:run, thread3:death, thread4:run, thread5:run
|
(b)mainは特に何もしていない
|
main:run, thread4:death, thread4:run
|
(b)
main:run, thread5:death
|
main:run

という風に遷移します。当然joinしている訳ではありません。
期待しているのは

main:run, thread1:run, thread2:run, thread3:run, thread4:run, thread5:run
|
main:run, thread1:death, thread2:death, thread3:death, thread4:death, thread5:death

という動きです。

奇妙なことに

スレッドの中では、

def run(limit)
  start = Time.now
  loop do
    break if (Time.now - start) >= limit
  end
end

的なメソッドが走っているのですが、デバッグの為にloopの中にpなどの出力処理を挿むと、期待している動きになります。
この挙動のせいで、スレッドは並列実行されているが、他の処理で待ちが発生していると解釈して、後処理のあたりを探っていたのですが、テストの結果から察するにやはり、pを挿まない方では、直列に動いてしまっていると分かり、試しにsleepを挿んでみると、期待通りに動きました。

全然奇妙じゃない

この動作が奇妙だと思ったのですが、良く考えてみると、もしコンテキストスイッチがされていないのだったら、I/Oを実行したり、sleepしたら強制的にコンテキストスイッチが働くはずなので、やはり何も挿んでいない時は、1つのスレッドがCPUを占有しているようです。

スレッド切り替えのタイミング

スレッドの切り替えのタイミングを調べてみました。
というかRHGの19章読んだだけですが。
http://i.loveruby.net/ja/rhg/book/thread.html

RHGによると、スレッドの切り替えのトリガとなるのは以下の4つの場面だそうです。

I/O待ち

文字通りで、getcなどで入力待ちになると、その間は処理がないので他のスレッドを動かします。

別スレッド待ち

Thread#joinを呼んだときはこの状態になります。

時間待ち

sleepしている間は、他のスレッドに移ります

時間切れ

上の3つは明示的にスレッドの切り替えが行われますが、これではあるスレッドがCPUを占有し続ける状況になりえます。
そこで、一定時間ごとにスレッド切り替えをする機構が用意されています。

結局

切り替えのタイミングを考えると、上記のrunメソッドのloop内では切り替えが発生しないように思える。
つまり、タイムアウトするまでは同じスレッドで動き続けて正解なのだ。しかし、冷静に考えるとタイムアウトの時間は1秒とかそんな大きな値ではないはずなので、今回のコードで切り替わらないのはおかしい。現に再現させようと書いたコードでは、切り替えが正常に行われていた。


結局良くわかっていない。