LoginSignup
133
93

More than 3 years have passed since last update.

【Ruby】Thread(スレッド)を理解する

Last updated at Posted at 2018-07-23

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

スレッドを使用しない場合は通常通りプログラムは上から実行され、time1time2の出力する時間には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.newThread.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#runThread#wakeupで起こされたスレッドはこの状態になる。)
sleep 停止中(Thread.stopThread#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 現在のスレッドから他のスレッドに実行を移す

参考

Ruby-Doc(Thread)
Rubyリファレンス(Thread)

133
93
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
133
93