スレッド。
成果物
情報源
Thread
スレッドを表すクラスです。スレッドとはメモリ空間を共有して同時に実行される制御の流れです。 Thread を使うことで並行プログラミングが可能になります。
実装
ネイティブスレッドを用いて実装されていますが、現在の実装では Ruby VM は Giant VM lock (GVL) を有しており、同時に実行されるネイティブスレッドは常にひとつです。ただし、IO 関連のブロックする可能性があるシステムコールを行う場合には GVL を解放します。その場合にはスレッドは同時に実行され得ます。また拡張ライブラリから GVL を操作できるので、複数のスレッドを同時に実行するような拡張ライブラリは作成可能です。
基本的には事実上ひとつだけ。それでマルチコアを活かすことができるのか?
スケジューリング
Ruby のスレッドスケジューリングはネイティブスレッドのそれを利用しています。よって詳細はプラットフォームに依存します。
メインスレッド
プログラムの開始と同時に生成されるスレッドを「メインスレッド」と呼びます。なんらかの理由でメインスレッドが終了する時には、他の全てのスレッドもプログラム全体も終了します。ユーザからの割込みによって発生した例外はメインスレッドに送られます。
スレッドの終了
スレッドの起動時に指定したブロックの実行が終了するとスレッドの実行も終了します。ブロックの終了は正常な終了も例外などによる異常終了も含みます。
例外発生時のスレッドの振る舞い
あるスレッドで例外が発生し、そのスレッド内で rescue で捕捉されなかった場合、通常はそのスレッドだけがなにも警告なしに終了されます。ただしその例外で終了するスレッドを Thread#join で待っている他のスレッドがある場合、その待っているスレッドに対して、同じ例外が再度発生します。
Thread#joinの概要は以下。
スレッド self の実行が終了するまで、カレントスレッドを停止します。
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 などによって終了させられた時も同様に実行されます。
メインスレッドの終了時の詳細に関しては 終了処理 を参照して下さい。
スレッドの状態
個々のスレッドは、以下の実行状態を持ちます。これらの状態は Object#inspect や Thread#status によって見ることができます。
p Thread.new {sleep 1} # => #<Thread:0xa039de0 sleep>
状態 | 概要 | 詳細 |
---|---|---|
run |
実行/実行可能 | 生成されたばかりのスレッドや Thread#run や Thread#wakeup で起こされたスレッドはこの状態です。 Thread#join でスレッドの終了を待っているスレッドもスレッドの終了によりこの状態になります。 この状態のスレッドは「生きて」います。 |
sleep |
停止 | Thread.stop や Thread#join により停止されたスレッドはこの状態になります。 この状態のスレッドは「生きて」います。 |
aborting |
終了処理中 | Thread#kill 等で終了されるスレッドは一時的にこの状態になります。この状態から停止状態(sleep)になることもあります。 この状態のスレッドはまだ「生きて」います。 |
dead |
終了 | Thread#kill 等で終了したスレッドはこの状態になります。この状態のスレッドはどこからも参照されていなければ GC によりメモリ上からなくなります。 この状態のスレッドは「死んで」います。 |
デッドロックの検出 @todo
メンバ抜粋
特異メソッド
DEBUG DEBUG= abort_on_exception abort_on_exception= current exclusive exit fork handle_interrupt kill list main new pass pending_interrupt? report_on_exception report_on_exception= start stop
インスタンスメソッド
[] []= abort_on_exception abort_on_exception= add_trace_func alive? backtrace backtrace_locations exit fetch group inspect join key? keys kill name name= pending_interrupt? priority priority= raise report_on_exception report_on_exception= run safe_level set_trace_func status stop? terminate thread_variable? thread_variable_get thread_variable_set to_s value wakeup
特異メソッド | インスタンスメソッド | 概要 |
---|---|---|
new , start /fork |
- | 生成&開始(initialize する/しない) |
stop |
run /wakeup |
停止。再起動(即/状態変更) |
exit ,kill |
exit ,kill ,terminate |
終了(カレント/指定) |
for i in 1..5 Thread.new(i) {|t| p t } Thread.start(i) {|t| p t } Thread.fork(i) {|t| p t } end
a = Thread.new { print "a"; Thread.stop; print "c" } sleep 0.1 while a.status!='sleep' print "b" a.run a.join # => "abc"
th1 = Thread.new do begin sleep 10 p 'これが表示される前にキルされるはず。' ensure p "this will be displayed" end end sleep 0.1 th1.kill
所感
Rubyは並列実行できないのかな?
対象環境
- Raspbierry pi 4 Model B
- Raspberry Pi OS buster 10.0 2020-08-20 ※
- bash 5.0.3(1)-release
- Ruby 3.0.2
$ uname -a Linux raspberrypi 5.10.52-v7l+ #1441 SMP Tue Aug 3 18:11:56 BST 2021 armv7l GNU/Linux