Threadの中で即座に例外をあげる
どういうことかというと、Threadを作って、
その中で例外が上がるようなものを作ったとしても、
joinしないと例外が上がりません。
joinを前提にしていないループするプログラムなどは
困ります。
例えばこんなヤツ。
ネットワークが切れたら例外が上がって、
最初から処理をやり直したいって場合。
#!/usr/bin/env ruby # require 'net/http' begin http=Net::HTTP.new("d.hatena.ne.jp","80") Net::HTTP.version_1_2 t=Thread.new do loop do timeout(5) do http.start do |http| req =Net::HTTP::Post.new("/xibbar") response = http.request(req) end end p :thread_loop sleep 10 end end loop do p :mail_loop sleep 3 end rescue Timeout::Error =>ex puts ex.message puts "Timeout Error" sleep 10 retry rescue =>ex puts ex.message puts "Network unreacheable." sleep 10 retry end
ネットワークがつながっている場合は、
% ruby sample4.rb :mail_loop :thread_loop :mail_loop :mail_loop :mail_loop :thread_loop :mail_loop
こんな感じです。
んで、このプログラムには重大な欠陥があって、
joinするまで、例外が上がらないのだ。
LANケーブルを外してやってみると、、、
% ruby sample4.rb :mail_loop :mail_loop :mail_loop :mail_loop :mail_loop :mail_loop :mail_loop :mail_loop :mail_loop :mail_loop :mail_loop
mainの部分だけループしているのがわかります。
threadのループの中で例外が上がったら
即座にrescueしたいのに、joinするのを待ってしまいます。
これを回避するには、
Thread#abort_on_exception=true
を入れます。こうすると
% ruby sample.rb :mail_loop getaddrinfo: nodename nor servname provided, or not known Network unreacheable. :mail_loop getaddrinfo: nodename nor servname provided, or not known Network unreacheable. :mail_loop getaddrinfo: nodename nor servname provided, or not known Network unreacheable. :mail_loop :thread_loop :mail_loop
となり、意図した挙動になりました。
これは実行してからLANケーブルを入れた挙動です。
というわけで、きちんと動作するコード
#!/usr/bin/env ruby # require 'net/http' begin http=Net::HTTP.new("d.hatena.ne.jp","80") Net::HTTP.version_1_2 t=Thread.new do loop do timeout(5) do http.start do |http| req =Net::HTTP::Post.new("/xibbar") response = http.request(req) end end p :thread_loop sleep 10 end end t.abort_on_exception=true loop do p :mail_loop sleep 3 end rescue Timeout::Error =>ex puts ex.message puts "Timeout Error" sleep 10 retry rescue =>ex puts ex.message puts "Network unreacheable." sleep 10 retry ensure t.kill end
abort_on_exception=を追加して、
ensureでkillするようにしました。