#Thread(スレッド)とは?
Thread(スレッド)とはプログラムの一連の処理のまとまり
のことです。
複数の処理の流れ(スレッド)を持つプログラムをマルチスレッドのプログラム
と呼び、複数の処理を並行して実行させるプログラミングのことを並行(concurrent)プログラミング
と呼びます。
RubyのThreadクラスはこの並行(concurrent)プログラミング
を実現するために使用されます。
#Rubyでマルチスレッドを作成する。
プログラム開始時に生成されるスレッドはメインスレッド
、現在実行中のスレッドはカレントスレッド
と呼ばれます。
RubyではThread#main
を用いる事でメインスレッド
を確認できます。
また、Thread#list
でプログラム上に存在するスレッドが配列で表示されます。
p Thread.main #=> #<Thread:0x00007f9600882f00 run> #プログラム開始時に作成されるメインスレッド
p Thread.list #=> [#<Thread:0x00007f9600882f00 run>] #スレッドは1つのみ。
p "Hello World" #=> "Hello World"
上記の例ではプログラミング開始時に作成されたメインスレッド
1つのみが存在している事が確認できます。
それでは、実際にRubyでマルチスレッドのプログラム
を作成してみましょう。
RubyではThread#new
/Thread.fork
/Thread.start
のいずれかでスレッドを作成可能です。
p Thread.main #=> #<Thread:0x00007f9d5d082ee0 run> #メインスレッド
Thread.new { 'Hello World' } #別のスレッドの作成
Thread.fork { 'Good Morning World' } #別のスレッドの作成②
#メインスレッドに加えて上で作成した2つのスレッドが配列に格納されている。
p Thread.list #=> [#<Thread:0x00007f9d5d082ee0 run>, #<Thread:0x00007f9d5d113580@code11.rb:2 run>, #<Thread:0x00007f9d5d1133a0@code11.rb:3 dead>]
メインスレッドに加えて作成した他2つのスレッドが配列に格納されていることが確認できます。
Rubyでマルチスレッド
を生成することが出来ました。
#Rubyで並行プログラミングの実行
それでは、スレッドを使用することで「何が出来るのか」、「並行プログラミングとは何か」をサンプルコードを交えて説明します。
以下のコードを用いて、スレッドを使用しない場合とした場合の出力結果の違いを確認してみます。
def time1
sleep(2)
puts Time.now.strftime("%H時%M分%S秒")
end
def time2
sleep(2)
puts Time.now.strftime("%H時%M分%S秒")
end
スレッドを使用しない場合は通常通りプログラムは上から実行され、time1
とtime2
の出力する時間にはKernel#sleep
で待機させた2秒の時間差が生まれます。
def time1
sleep(2)
puts Time.now.strftime("%H時%M分%S秒")
end
def time2
sleep(2)
puts Time.now.strftime("%H時%M分%S秒")
end
#何も指示がない場合、プログラムは上から下へと実行されていく。
time1 #=> 13時23分30秒
time2 #=> 13時23分32秒
以下がスレッドを使用して実行した結果です。
Thread#join
を用いる事でメインスレッド
の実行を中断し、指定されたスレッドを実行、終了することができます。
def time1
sleep(2)
puts Time.now.strftime("%H時%M分%S秒")
end
def time2
sleep(2)
puts Time.now.strftime("%H時%M分%S秒")
end
#スレッドを使用しない場合
time1 #=> 13時23分30秒
time2 #=> 13時23分32秒
## Threadを利用した例
threads = []
threads << Thread.new { time1() }
threads << Thread.fork { time2() }
threads.each { |thr| thr.join }
#=> 13時23分34秒
#=> 13時23分34秒
スレッドを使用した場合は上のように複数の処理を並行に
実行することが可能です。
これによってtime1とtime2の出力結果が同じものになりました。
#Thread(スレッド)の生成
上記でも記載しましたが、以下のサンプルコード上の方法でスレッドを生成することが可能です。
スレッドを生成するメソッドの後にブロックを伴わない場合には作成できずThreadError
を引き起こすので注意が必要です。
また、Thread.new
とThread.start
/Thread.fork
は微妙に意味が異なります。詳しくは以下のリンク先を参考にしてください。
Rubyリファレンス
Thread.new {} #=> #<Thread:0x00007fdf4c154e00@code11.rb:1 run>
Thread.start {} #=> #<Thread:0x00007fdf4c154ab8@code11.rb:2 run>
Thread.fork {} #=> #<Thread:0x00007fdf4c154860@code11.rb:3 run>
thr = Thread.new #=> `initialize': must be called with a block (ThreadError)
#Thread(スレッド)のライフサイクル
Thread(スレッド)には以下のような5つの状態があります。
あるスレッドの状態はライフサイクルの中で変化していきます。
<Thread:0x00007fdf4c154860@code11.rb:3 run>
の末尾にあるrun
がスレッドの状態を表しています。
状態 | 意味 |
---|---|
run | 実行可能、または実行中(生成直後やThread#run``Thread#wakeup で起こされたスレッドはこの状態になる。) |
sleep | 停止中(Thread.stop やThread#join により一時停止されたスレッドはこの状態になる。) |
aborting | 終了処理中(Thread#kill などで終了されるスレッドは一時的にこの状態になる。) |
false | 正常に終了した(Thread#kill で終了したり、正常終了したスレッドの場合falseが返る。) |
nil | 例外などで異常終了した場合にはnilが返る。 |
以下のメソッドでスレッドの状態を確認することができます。
状態 | 意味 |
---|---|
status | 状態を文字列で返す。 |
alive? | スレッドが生きているかどうかをtrueかfalseで返す。 |
stop? | スレッドが停止しているかどうかをtrueかfalseで返す。 |
#Thread(スレッド)の操作
以下のようなメソッドでスレッドの状態の操作が可能です。
状態 | 意味 |
---|---|
wakeup | 停止スレッドを実行可能状態(run)にする。 |
run | 即座に処理をそのスレッドに切り替える。 |
kill/terminate/exit | 生きている状態のスレッドの終了/既に終了しているスレッドに対しては何も行わない。 |
stop | 実行したスレッド自身を停止状態にする。 |
pass | 現在のスレッドから他のスレッドに実行を移す |