複数のプロセスで並列に処理することをマルチスレッドと呼びますが、Rubyのスレッドオブジェクトを使えば、簡単にマルチスレッド処理が作れます。マルチスレッドのスキルを身に付けたい方は、ぜひ、Rubyのスレッドオブジェクトを利用してください。
今回はRubyにおけるマルチスレッド処理の作り方を紹介します。
Rubyのスレッドを理解する
Rubyはマルチスレッドをサポートしており、1つのプログラムから複数の処理を並列に実行させることが可能です。ではマルチスレッドのスレッドとはどのような意味の言葉かご存じでしょうか。まずはスレッドの意味について解説します。
そもそもスレッドとは
コンピュータ用語のスレッドとは、一言で言えばCPU利用の単位です。そもそもコンピュータプログラムとは、CPUの命令の組み合わせを人が読めるようにしたものです。Rubyのプログラムも、最終的にCPUの命令の組み合わせに変換されて実行されます。
その際、通常は1つのCPUが実行できる命令の組み合わせとして作られますが、複数のCPUに別の命令の組み合わせとして実行されるプログラムも作れます。そして、そのような1つのCPUで実行される命令の組み合わせがスレッドです。
Rubyのスクリプトを実行する場合、1つのCPUで実行される命令の組み合わせに変換されるので、通常スレッドは1つだけです。しかし、Rubyなら1つのスレッドから複数のスレッドを実行させる、マルチスレッド処理も作れます。
マルチスレッドなプログラムとは
マルチスレッドのプログラムは、並列処理を実現するための仕組みです。なお並列処理とは、1つの目的のために複数のCPUを利用するための仕組みで、いろいろな用途で利用できます。
例えば、Webアプリケーションでリクエストに答えるスレッドとデータをファイルに書き込むスレッドとを別に実行すれば、書き込みが終わるまで待たずにリクエストに答えられます。
このようにファイルへの書き込み処理やネットワークを介した通信処理など、CPUの能力に大して速度の遅いデバイスを扱う処理にマルチスレッドを使うことで、利用者のレスポンスを改善させることが可能です。
スレッドを扱うクラス
通常のRubyのプログラムでスレッドを意識する機会は少ないでしょう。しかしマルチスレッドのプログラムを作る場合、スレッドを意識したプログラミングが必要です。そしてRubyでスレッドを扱う場合、スレッド(Thread)クラスを使います。
なおスレッド(Thread)クラスはRubyの標準に組み込まれているので、Rubyが使える環境であればどこでもRubyによる並列処理のプログラムが作成可能です。
スレッドクラスの基本
Rubyでマルチスレッドを活用したプログラムを作るには、スレッド(Thread)クラスのオブジェクトを利用します。スレッドクラスのオブジェクトの使い方をマスタして、マルチスレッドを使いこなせるようになりましょう。
次からスレッドクラスの使い方について紹介します。
スレッドオブジェクトの使い方
Rubyでマルチスレッドのプログラムを作るなら、スレッドクラスの使い方をマスターしましょう。なおRubyで扱う変数は全てオブジェクトです。スレッドを扱う場合も、まずスレッドクラスのオブジェクトを作り、続いてそのスレッドを制御します。
そしてスレッドクラスのオブジェクトを作るにはnewメソッドを利用してください。
スレッドオブジェクトの使い方の基本
スレッドオブジェクト = Thread.new do
(ここにスレッドで実行する内容を記述する)
end
スレッドオブジェクト.スレッド制御用のメソッド
スレッドを制御する
スレッドオブジェクトを作成したら、そのスレッドを実行したり、スレッドの状態をチェックしたり、停止させる、といったことが可能です。そして、そのようなスレッドの制御には、スレッドオブジェクトのメソッドを利用します。
代表的なスレッドオブジェクトのメソッドを次に紹介します。
joinメソッド:スレッドの終了まで待機する。
runメソッド:スレッドを実行する。
exitメソッド:スレッドを停止する(killメソッド、terminateメソッドも同じ)
statusメソッド:生きているスレッドの状態を”run”、”sleep”, “aborting” のいずれかで返す。
alive?mesoddo :スレッドが生きている状態でtrueを返す。
stop?メソッド:スレッドが終了しているか停止状態でtrueを返す
マルチスレッドの例
次に先ほど紹介したRubyのスレッドオブジェクトを利用し、スレッドを生成したり削除するマルチスレッド処理の例を紹介します。
2つのスレッドを並列実行する例
次に、th1とth2のスレッドを生成し、th1スレッドが終了したら、th2スレッドも停止するマルチスレッドなプログラムを紹介します。
th1 = Thread.new do for i in 1..100 strs = "No." + i.to_s + " : " + Time.now.strftime("%H:%M:%S") puts strs sleep(1) end end th2 = Thread.new do sleep(3) puts "スレッド2が終了" end puts "マルチスレッド実行中" th1.run th2.join if th1.alive? then th1.kill puts "マルチスレッド終了" end
生成する2つのスレッドの違い
上の例ではth1とth2の2つのスレッドを生成し、それぞれ実行していますがその内容は、th1が1秒間隔で時刻を表示し、th2が5秒待機してメッセージを表示します。そして2つともnewメソッドで生成します。
ただし、2つのスレッドの実行が違っており、th1はth1.runを、またth2はth2.joinを利用しています。この違いによりth1が実行されても待機しませんが、続くth2の実行後、joinメソッドのため待機します。
さらにこのプログラムでは、th1が生きているかをチェックしており、th1が終了していればkillメソッドでth1スレッドを終了させます。
この例の実行結果
この例を実行すると、まずはメインのプログラムが「マルチスレッド実行中」を表示します。そしてth1により1秒毎に時刻を表示し、th2により5秒後に「スレッド2が終了」を表示します。th2が終了した時点でth1も終了するので、メインのプログラムが「マルチスレッド終了」を表示して終了します。
マルチスレッドプログラム例の実行結果
マルチスレッド実行中
No.1 : 14:00:01
No.2 : 14:00:02
No.3 : 14:00:03
スレッド2が終了
マルチスレッド終了
スレッドと情報をやりとりする
先ほどの例ではスレッド毎に別々に標準出力に文字列を表示していましたが、Queueクラスを使用することでスレッドとメインプログラム間で情報のやりとりが可能です。
次にQueueスレッドを利用して、スレッドからメッセージを受け取り、それをメインのプログラムで表示するように改造した例を次に紹介します。
q1 = Queue.new q2 = Queue.new th1 = Thread.new do for i in 1..100 sleep(1) strs = "No." + i.to_s + " : " + Time.now.strftime("%H:%M:%S") q1.push(strs) end end th2 = Thread.new do sleep(3) q2.push("スレッド2が終了") end puts "マルチスレッド実行中" th1.run th2.run while th2.alive? puts q1.pop end puts q2.pop if th1.alive? then th1.kill puts "マルチスレッド終了" end
上の例ではQueueクラスのオブジェクトをスレッド毎に作成し、pushメソッドとpopメソッドとでメッセージを送受信しながらマルチスレッドを実行する例です。先ほどの例と同じようにth2が終了した時点でth1も終了させます。
例の実行結果
マルチスレッド実行中
No.1 : 14:10:01
No.2 : 14:10:02
No.3 : 14:10:03
スレッド2が終了
マルチスレッド終了
まとめ
これまで紹介したようにRubyでマルチスレッドのプログラムを作るならスレッドクラスのオブジェクトを利用します。またこのオブジェクトにより生成したスレッドと情報をやりとりする処理も作れることから、Webアプリケーションでも非同期処理や処理を分散して実行する場合などで利用可能です。
Rubyならマルチスレッド処理を簡単に作れるので、ぜひその仕組みを理解して、Ruby on RailsによるWebアプリケーションの処理に活用してください。
英語のthreadにはいろいろな意味がありますが、よく使われるのは糸という意味です。糸そのものとして使われることもありますが、糸を通す、筋道、脈絡といった意味でも使われます。そして、コンピュータ用語で使われるthreadは、「thread of execution」(実行の脈絡)を略した言葉です。