当たり前と言えば当たり前なんですけど。
スレッドをまたぐ大域脱出
Threadブロックよりも外にあるcatch(:out_of_thread)へ脱出しようとするとArgumentErrorエラーになります。
pry(main)> catch(:out_of_thread) do pry(main)* Thread.start do pry(main)* catch(:inside_of_thread) do pry(main)* loop do pry(main)* sleep 2 pry(main)* throw(:out_of_thread) pry(main)* end pry(main)* end pry(main)* end pry(main)* p Thread.list pry(main)* Thread.list.each{ |th| th.join unless th == Thread.main} pry(main)* end [#<Thread:0x007f9dda0677a8 run>, #<Thread:0x007f9ddc2256b0 run>] ArgumentError: uncaught throw :out_of_thread
スレッドをまたがない大域脱出
Threadブロックの内側にあるcatch(:inside_of_thread)へ脱出する場合には問題ありません。
pry(main)> catch(:out_of_thread) do pry(main)* Thread.start do pry(main)* catch(:inside_of_thread) do pry(main)* loop do pry(main)* sleep 2 pry(main)* throw(:inside_of_thread) pry(main)* end pry(main)* end pry(main)* end pry(main)* p Thread.list pry(main)* Thread.list.each{ |th| th.join unless th == Thread.main} pry(main)* end [#<Thread:0x007f9dda0677a8 run>, #<Thread:0x007f9dda426ee8 run>] => [#<Thread:0x007f9dda0677a8 run>, #<Thread:0x007f9dda426ee8 dead>]
これで、throwすることにより、スレッド内の処理を全てすっ飛ばすことができます。
参照
module function Kernel.#throw
Kernel.#catchとの組み合わせで大域脱出を行います。 throw は同じ tag を指定した catch のブロックの終わりまでジャンプします。
throw は探索時に呼び出しスタックをさかのぼるので、 ジャンプ先は同じメソッド内にあるとは限りません。 もし ensure節 が存在するならジャンプ前に実行します。
同じ tag で待っている catch が存在しない場合は、例外で スレッドが終了します。
module function Kernel.#throw
class ThreadError
- クラスの継承リスト: ThreadError < StandardError < Exception < Object < Kernel < BasicObject
要約
Thread 関連のエラーが起きたときに発生します。
- カレントスレッドを Thread#join しようとしたとき
- Thread#join でデッドロックしそうになったとき
- 終了したスレッドを Thread#wakeup あるいは Thread#run しようとしたとき
- スレッドが一つしかないのに Thread.stop しようとしたとき
- Kernel.#throw がスレッド内で Kernel.#catch されないとき
- スレッドから return しようとしたとき
- イテレータを与えずにスレッドを生成しようとしたとき
- カレントスレッドの属するスレッドグループが freeze されているときに、スレッドを生成しようとしたとき
- freeze あるいは enclose されているスレッドグループにスレッドを加えようとした時
ThreadErrorじゃなくArgumentErrorでしたですね。なんでかな。
そういえばスレッド内で生じたエラーは、エラーとして扱われず終わっちゃうんですね、少し忘れていました。
例外発生時のスレッドの振る舞い
あるスレッドで例外が発生し、そのスレッド内で rescue で捕捉されなかっ た場合、通常はそのスレッドだけがなにも警告なしに終了されます。ただ しその例外で終了するスレッドを Thread#join で待っている他の スレッドがある場合、その待っているスレッドに対して、同じ例外が再度 発生します。
begin t = Thread.new do Thread.pass # メインスレッドが確実にjoinするように raise "unhandled exception" end t.join rescue p $! # => "unhandled exception" endまた、以下の 3 つの方法により、いずれかのスレッドが例外によって終 了した時に、インタプリタ全体を中断させるように指定することができま す。
- 組み込み変数 $DEBUG を真に設定する(デバッグモード) ruby インタプリタを -d オプション 付きで起動した場合も同様。 (オプションの詳細に関してはRubyの起動 を参照)
- Thread.abort_on_exception でフラグを設定する。
- Thread#abort_on_exception で指定 したスレッドのフラグを設定する。
上記3つのいずれかが設定されていた場合、インタプリタ全体が中断されます。
class Thread