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

#Thread(スレッド)とは？
Thread(スレッド)とは`プログラムの一連の処理のまとまり`のことです。
複数の処理の流れ(スレッド)を持つプログラムを`マルチスレッドのプログラム`と呼び、複数の処理を並行して実行させるプログラミングのことを`並行(concurrent)プログラミング`と呼びます。
RubyのThreadクラスはこの`並行(concurrent)プログラミング`を実現するために使用されます。

#Rubyでマルチスレッドを作成する。
プログラム開始時に生成されるスレッドは`メインスレッド`、現在実行中のスレッドは`カレントスレッド`と呼ばれます。
Rubyでは`Thread#main`を用いる事で`メインスレッド`を確認できます。
また、`Thread#list`でプログラム上に存在するスレッドが配列で表示されます。

```ruby
p Thread.main #=> #<Thread:0x00007f9600882f00 run> #プログラム開始時に作成されるメインスレッド
p Thread.list #=> [#<Thread:0x00007f9600882f00 run>] #スレッドは１つのみ。
p "Hello World" #=> "Hello World"
```

上記の例ではプログラミング開始時に作成された`メインスレッド`１つのみが存在している事が確認できます。
それでは、実際にRubyで`マルチスレッドのプログラム`を作成してみましょう。
Rubyでは`Thread#new`/`Thread.fork`/`Thread.start`のいずれかでスレッドを作成可能です。

```ruby
p Thread.main #=> #<Thread:0x00007f9d5d082ee0 run>　＃メインスレッド
Thread.new { 'Hello World' } #別のスレッドの作成
Thread.fork { 'Good Morning World' } #別のスレッドの作成②

#メインスレッドに加えて上で作成した２つのスレッドが配列に格納されている。
p Thread.list #=> [#<Thread:0x00007f9d5d082ee0 run>, #<Thread:0x00007f9d5d113580@code11.rb:2 run>, #<Thread:0x00007f9d5d1133a0@code11.rb:3 dead>]
```
メインスレッドに加えて作成した他２つのスレッドが配列に格納されていることが確認できます。
Rubyで`マルチスレッド`を生成することが出来ました。

#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秒の時間差が生まれます。

```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 #=> 13時23分30秒
time2 #=> 13時23分32秒
```

以下がスレッドを使用して実行した結果です。
`Thread#join`を用いる事で`メインスレッド`の実行を中断し、指定されたスレッドを実行、終了することができます。

```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 #=> 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リファレンス](https://docs.ruby-lang.org/ja/latest/method/Thread/s/fork.html)

```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(スレッド）には以下のような５つの状態があります。
あるスレッドの状態はライフサイクルの中で変化していきます。
`<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|現在のスレッドから他のスレッドに実行を移す|

#参考
[Ruby-Doc(Thread)](https://ruby-doc.org/core-2.2.0/Thread.html)
[Rubyリファレンス(Thread)](https://docs.ruby-lang.org/ja/latest/class/Thread.html)