DeadLockInRuby
RubyはRubyスレッドのデッドロックの検出機構を持つ
この記事ではRubyによるデッドロック検出時のエラーメッセージを説明する。
サンプル
以下のスクリプトを実行するとデッドロックが検出される。
#!/usr/local/bin/ruby -Ks require 'thread' q = Queue.new Thread.new do q.deq end.join
このプログラムでは、メインスレッドはワーカスレッドの終了をThread#joinによって待っている。その一方でワーカスレッドはQueue#deqを呼ぶことでデータのエンキューを待つ。したがってどのスレッドも動けない。
この時、Rubyは以下のメッセージを表示して実行を中断する。
test>ruby dl.rb deadlock 0xf63c8: sleep:- - /usr/local/lib/ruby/1.8/thread.rb:318 deadlock 0x10d678: sleep:J(0xf63c8) (main) - dl.rb:5 dl.rb:5:in `join': Thread(0x10d678): deadlock (fatal) from dl.rb:5
出力されるメッセージの読み方
deadlockという行はすべてのスレッドの状態を示す。
deadlock 0xf63c8: sleep: - - /usr/local/…… A B C D E
- A: スレッドの参照
- B: スレッドの状態
- C: スレッドの待ち状態理由
- F(x): ファイルディスクリプタxで待ち
- S: IOセレクション
- T(x): タイムアウト待ち(xミリ秒)
- J(x): スレッドxに対してjoin
- P: プロセスウェイト
- -: それ以外
- D: メインスレッドかそれ以外か
- E: ソースファイル:行番号
メッセージ詳解
deadlock 0xf63c8: sleep:- - /usr/local/lib/ruby/1.8/thread.rb:318 deadlock 0x10d678: sleep:J(0xf63c8) (main) - dl.rb:5
まず、Dが(main)のメッセージに着目する。例では2行目のメッセージがメインスレッドの状態を示すメッセージである。
Eのソースファイルと行番号から、Thread.new ... end.join のjoinが問題だということがわかる。
メッセージのC(スレッドの待ち状態理由)は、J(0xf63c8)であるから、スレッド0xf63c8に対するjoinだということがわかる(joinだということはソースファイルの行からもわかる)。
次にスレッド0xf63c8を見るとthread.rbの318行目で-(その他)の理由でsleepしていることがわかる。 該当行は、Queue#pop(deqにaliasされている)内なので、4行目のdeq呼び出しが原因ということがわかる。
後は何が悪いか自分で考える。
Keyword(s):
References: