要約
スレッドを表すクラスです。スレッドとはメモリ空間を共有して同時に実行される制御の流れです。 Thread を使うことで並行プログラミングが可能になります。
実装
Ruby のスレッドはユーザレベルで実装されており、全プラットホーム上において同じ挙動を示します。 Ruby インタプリタは時分割でスレッドを実行しますので、スレッドを使うことで実行速度が速くなることはありません。
スケジューリング
Ruby のスレッドスケジューリングは優先順位付のラウンドロビンです。一定時間毎、あるいは実行中のスレッドが権利を放棄したタイミングでスケジューリングが行われ、その時点で実行可能なスレッドのうち最も優先順位が高いものにコンテキストが移ります。
メインスレッド
プログラムの開始と同時に生成されるスレッドを「メインスレッド」と呼びます。なんらかの理由でメインスレッドが終了する時には、他の全てのスレッドもプログラム全体も終了します。ユーザからの割込みによって発生した例外はメインスレッドに送られます。
スレッドの終了
スレッドの起動時に指定したブロックの実行が終了するとスレッドの実行も終了します。ブロックの終了は正常な終了も例外などによる異常終了も含みます。
例外発生時のスレッドの振る舞い
あるスレッドで例外が発生し、そのスレッド内で 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つのいずれかが設定されていた場合、インタプリタ全体が中断されます。
スレッド終了時の ensure 節の実行
スレッド終了時には ensure 節が実行されます。これはスレッドが正常に終了する時はもちろんですが、他のスレッドから Thread#kill などによって終了させられた時も同様に実行されます。 Thread#kill! が呼ばれた時はensure 節が実行されません。
ただしメインスレッドに対して Thread#kill! が呼ばれても ensure 節が実行されます。メインスレッドの終了時の詳細に関しては 終了処理 を参照して下さい。
スレッドの状態
個々のスレッドは、以下の実行状態を持ちます。これらの状態は Object#inspect や Thread#status によって見ることができます。
p Thread.new {sleep 1} # => #<Thread:0xa039de0 sleep>
- run (実行or実行可能状態)
-
生成されたばかりのスレッドや Thread#run や Thread#wakeup で起こされたスレッドはこの状態です。 Thread#join でスレッドの終了を待っているスレッドもスレッドの終了によりこの状態になります。 この状態のスレッドは「生きて」います。
- sleep (停止状態)
-
Thread.stop や Thread#join により停止されたスレッドはこの状態になります。 この状態のスレッドは「生きて」います。
- aborting (終了処理中)
-
Thread#kill 等で終了されるスレッドは一時的にこの状態になります。この状態から停止状態(sleep)になることもあります。 この状態のスレッドはまだ「生きて」います。
- dead (終了状態)
-
Thread#kill 等で終了したスレッドはこの状態になります。この状態のスレッドはどこからも参照されていなければ GC によりメモリ上からなくなります。 この状態のスレッドは「死んで」います。
デッドロックの検出
ruby はデッドロックを検出します。デッドロックを検出した場合、例外 fatal が発生してプロセスは終了します。デッドロックの条件は以下のとおりです。
- スレッドが複数ある
- すべてのスレッドが sleep (停止状態) である
- すべてのスレッドが IO 待ちでない
メインスレッドだけが Thread.stop で停止している状態は sleep forever と同じと見なし、 fatal は発生しません。
目次
- 特異メソッド
- インスタンスメソッド
- 追加されるメソッド
特異メソッド
abort_on_exception -> bool[permalink][rdoc]abort_on_exception=(newstate)-
真の時は、いずれかのスレッドが例外によって終了した時に、インタプリタ全体を中断させます。false の場合、あるスレッドで起こった例外は、Thread#join などで検出されない限りそのスレッドだけをなにも警告を出さずに終了させます。
デフォルトは false です。
Thread/例外発生時のスレッドの振る舞いを参照してください。
- [PARAM] newstate:
- スレッド実行中に例外発生した場合、インタプリタ全体を終了させるかどうかを true か false で指定します。
critical -> bool[permalink][rdoc]critical=(newstate)-
真である間、スレッドの切替えを行いません。
カレントスレッドが停止状態になった場合や、シグナルに割り込まれた場合には、自動的に false になります。ただし、Thread.new によりスレッドを生成した場合には、critical の値に関わらず そのスレッドは実行されます。また、Thread.pass により明示的に切替えることもできます。
デフォルトは false です。
注意: I/O や GC、拡張ライブラリがからむとこのフラグは無視されることもあります。排他制御を行うにはこのメソッドに頼らず Mutex や Monitor を使うべきです。
- [PARAM] newstate:
- スレッドの切替えを許すかどうかを、true か false で指定します。
current -> Thread[permalink][rdoc]-
現在実行中のスレッド(カレントスレッド)を返します。
p Thread.current #=> #<Thread:0x4022e6fc run>
exit -> ()[permalink][rdoc]-
カレントスレッドに対して Thread#exit を呼びます。
start(*arg) {|*arg| ... } -> Thread[permalink][rdoc]fork(*arg) {|*arg| ... } -> Thread-
スレッドを生成して、ブロックの評価を開始します。生成したスレッドを返します。
基本的に Thread.new と同じですが、 new メソッドと違い initialize メソッドを呼びません。
- [PARAM] arg:
- 引数 arg はそのままブロックに渡されます。スレッドの開始と同時にそのスレッド固有のローカル変数に値を渡すために使用します。
- [EXCEPTION] ThreadError:
- 現在のスレッドが属する ThreadGroup が freeze されている場合に発生します。またブロックを与えられずに呼ばれた場合にも発生します。
注意:
例えば、以下のコードは間違いです。スレッドの実行が開始される前に変数 i が書き変わる可能性があるからです。
for i in 1..5 Thread.start { p i } end上の例は以下のように書き直すべきです。
for i in 1..5 Thread.start(i) {|t| p t } end kill(thread) -> Thread[permalink][rdoc]-
指定したスレッド thread に対して Thread#exit を呼びます。終了したスレッドを返します。
- [PARAM] thread:
- 終了したい Thread オブジェクトを指定します。
th = Thread.new do end p Thread.kill(th) #=> #<Thread:0x40221bc8 dead>
list -> [Thread][permalink][rdoc]-
全ての生きているスレッドを含む配列を生成して返します。aborting 状態であるスレッドも要素に含まれます。
Thread.new do sleep end sleep 0.1 p Thread.list #=> [#<Thread:0x40377a54 sleep>, #<Thread:0x4022e6fc run>]
main -> Thread[permalink][rdoc]-
メインスレッドを返します。
p Thread.main #=> #<Thread:0x4022e6fc run>
new(*arg) {|*arg| ... } -> Thread[permalink][rdoc]-
スレッドを生成して、ブロックの評価を開始します。生成したスレッドを返します。
- [PARAM] arg:
- 引数 arg はそのままブロックに渡されます。スレッドの開始と同時にそのスレッド固有のローカル変数に値を渡すために使用します。
- [EXCEPTION] ThreadError:
- 現在のスレッドが属する ThreadGroup が freeze されている場合に発生します。またブロックを与えられずに呼ばれた場合にも発生します。
注意:
例えば、以下のコードは間違いです。スレッドの実行が開始される前に変数 i が書き変わる可能性があるからです。
for i in 1..5 Thread.new { p i } end上の例は以下のように書き直すべきです。
for i in 1..5 Thread.new(i) {|t| p t } end pass -> nil[permalink][rdoc]-
他のスレッドに実行権を譲ります。実行中のスレッドの状態を変えずに、他の実行可能状態のスレッドに制御を移します。
Thread.new do (1..3).each{|i| p i Thread.pass } exit end loop do Thread.pass p :main end #=> 1 :main 2 :main 3 :main stop -> nil[permalink][rdoc]-
他のスレッドから Thread#run メソッドで再起動されるまで、カレントスレッドの実行を停止します。
インスタンスメソッド
self[name] -> object | nil[permalink][rdoc]-
name に対応したスレッドに固有のデータを取り出します。 name に対応するスレッド固有データがなければ nil を返します。
- [PARAM] name:
- スレッド固有データのキーを文字列か Symbol で指定します。
self[name] = val[permalink][rdoc]-
val を name に対応するスレッド固有のデータとして格納します。
- [PARAM] name:
- スレッド固有データのキーを文字列か Symbol で指定します。文字列を指定した場合は String#to_sym によりシンボルに変換されます。
- [PARAM] val:
- スレッド固有データを指定します。nil を指定するとそのスレッド固有データは削除されます。
abort_on_exception -> bool[permalink][rdoc]abort_on_exception=(newstate)-
真の場合、そのスレッドが例外によって終了した時に、インタプリタ全体を中断させます。false の場合、あるスレッドで起こった例外は、Thread#join などで検出されない限りそのスレッドだけをなにも警告を出さずに終了させます。
デフォルトは偽です。Thread/例外発生時のスレッドの振る舞いを参照してください。
- [PARAM] newstate:
- 自身を実行中に例外発生した場合、インタプリタ全体を終了させるかどうかを true か false で指定します。
alive? -> bool[permalink][rdoc]-
スレッドが「生きている」時、true を返します。
Thread#status が真を返すなら、このメソッドも真です。
exit -> self[permalink][rdoc]kill -> selfterminate -> self-
スレッドの実行を終了させます。終了時に ensure 節が実行されます。
ただし、スレッドは終了処理中(aborting)にはなりますが、直ちに終了するとは限りません。すでに終了している場合は何もしません。このメソッドにより終了したスレッドの Thread#value の返り値は不定です。自身がメインスレッドであるか最後のスレッドである場合は、プロセスを Kernel.#exit(0) により終了します。
Kernel.#exit と違い例外 SystemExit を発生しません。
th1 = Thread.new do begin sleep 10 ensure p "this will be displayed" end end sleep 0.1 th1.kill #=> "this will be displayed"[SEE_ALSO] Thread#exit!, Kernel.#exit, Kernel.#exit!
exit! -> self[permalink][rdoc]kill! -> selfterminate! -> self-
ensure 節を実行せずにスレッドの実行を終了させます。
ただし、スレッドは終了処理中(aborting)にはなりますが、直ちに終了するとは限りません。すでに終了している場合は何もしません。このメソッドにより終了したスレッドの Thread#value の返り値は不定です。自身がメインスレッドであるか最後のスレッドである場合は、プロセスを Kernel.#exit(0) により終了します。
th1 = Thread.new do begin sleep 10 ensure p "th1: this will be displayed" end end th2 = Thread.new do begin sleep 10 ensure p "th2: this will NOT be displayed" end end th1.kill th2.kill! #=> "th1: this will be displayed"[SEE_ALSO] Thread#exit, Kernel.#exit, Kernel.#exit!
group -> ThreadGroup | nil[permalink][rdoc]-
スレッドが属している ThreadGroup オブジェクトを返します。死んでいるスレッドは nil を返します。
p Thread.current.group == ThreadGroup::Default # => true
inspect -> String[permalink][rdoc]-
自身を人間が読める形式に変換した文字列を返します。
join -> self[permalink][rdoc]join(limit) -> self | nil-
スレッド self の実行が終了するまで、カレントスレッドを停止します。self が例外により終了していれば、その例外がカレントスレッドに対して発生します。
limit を指定して、limit 秒過ぎても自身が終了しない場合、nil を返します。
- [PARAM] limit:
- タイムアウトする時間を整数か小数で指定します。単位は秒です。
- [EXCEPTION] ThreadError:
- join を実行することによってデッドロックが起きる場合に発生します。またカレントスレッドを join したときにも発生します。
以下は、生成したすべてのスレッドの終了を待つ例です。
threads = [] threads.push(Thread.new { n = rand(5); sleep n; n }) threads.push(Thread.new { n = rand(5); sleep n; n }) threads.push(Thread.new { n = rand(5); sleep n; n }) threads.each {|t| t.join} key?(name) -> bool[permalink][rdoc]-
name に対応したスレッドに固有のデータが定義されていれば true を返します。
- [PARAM] name:
- 文字列か Symbol で指定します。
keys -> [Symbol][permalink][rdoc]-
スレッド固有データに関連づけられたキーの配列を返します。キーは Symbol で返されます。
th = Thread.current th[:foo] = 'FOO' th['bar'] = 'BAR' p th.keys #=> [:bar, :foo]
priority -> Integer[permalink][rdoc]priority=(val)-
スレッドの優先度を返します。この値の大きいほど優先度が高くなります。メインスレッドのデフォルト値は 0 です。新しく生成されたスレッドは親スレッドの priority を引き継ぎます。
- [PARAM] val:
- スレッドの優先度を指定します。負の値も指定できます。
raise(error_type, message, traceback) -> ()[permalink][rdoc]-
自身が表すスレッドで強制的に例外を発生させます。
- [PARAM] error_type:
- Kernel.#raise を参照してください。
- [PARAM] message:
- Kernel.#raise を参照してください。
- [PARAM] traceback:
- Kernel.#raise を参照してください。
Thread.new { sleep 1 Thread.main.raise "foobar" } begin sleep rescue p $!, $@ end => #<RuntimeError: foobar> ["-:3"] run -> self[permalink][rdoc]-
停止状態(stop)のスレッドを再開させます。 Thread#wakeup と異なりすぐにスレッドの切り替えを行います。
- [EXCEPTION] ThreadError:
- 死んでいるスレッドに対して実行すると発生します。
safe_level -> Integer[permalink][rdoc]-
self のセーフレベルを返します。カレントスレッドの safe_level は、$SAFE と同じです。
セーフレベルについてはセキュリティモデルを参照してください。
status -> String | false | nil[permalink][rdoc]-
生きているスレッドの状態を文字列 "run"、"sleep", "aborting" のいずれかで返します。正常終了したスレッドに対して false、例外により終了したスレッドに対して nil を返します。
Thread#alive? が真を返すなら、このメソッドも真です。
stop? -> bool[permalink][rdoc]-
スレッドが終了(dead)あるいは停止(stop)している時、true を返します。
value -> object[permalink][rdoc]-
スレッド self が終了するまで待ち(Thread#join と同じ)、そのスレッドのブロックが返した値を返します。スレッド実行中に例外が発生した場合には、その例外を再発生させます。
スレッドが Thread#kill によって終了した場合は、返り値は不定です。
以下は、生成したすべてのスレッドの終了を待ち結果を出力する例です。
threads = [] threads.push(Thread.new { n = rand(5); sleep n; n }) threads.push(Thread.new { n = rand(5); sleep n; n }) threads.push(Thread.new { n = rand(5); sleep n; n }) threads.each {|t| p t.value}最後の行で、待ち合わせを行っていることがわかりにくいと思うなら以下のように書くこともできます。
threads.each {|t| p t.join.value} wakeup -> self[permalink][rdoc]-
停止状態(stop)のスレッドを実行可能状態(run)にします。
- [EXCEPTION] ThreadError:
- 死んでいるスレッドに対して実行すると発生します。
追加されるメソッド
exclusive { ... } -> object[permalink][rdoc] [added by thread]-
ブロック実行中、Threadの切り替えを行いません。