バナー画像

プログラミングをやっていれば「マルチスレッド処理」や「並列処理」という言葉を耳にした方も少なくないでしょう。これらは、複数の処理を同時に実行するときに用い、Webサーバーや、効率的な処理が求められるシステムでは、このような同時実行ができるような仕組みが用いられています。

Rubyで複数の処理を同時に実行する場合は「Thread」クラスを使用します。この記事では、Rubyで「Thread」クラスを作成して、複数の処理を同時実行する方法などを紹介していきます。

スレッドとは?

そもそもスレッドとは、プログラムの一連の処理のまとまりです。通常、プログラムを実行すると1つのスレッド(メインスレッド)が作成され、1つのスレッドの中ではプログラムは記述した順に実行されます。また、1つのスレッドの中では同時に複数の処理は実行できません。

例えば、Webサーバー上で動くアプリケーションの場合では、不特定多数のユーザーからのアクセスを処理しなければなりませんが、1つのスレッドですべてのユーザーの要求を順番に処理しているようでは、とてもではありませんが処理が間に合いません。

そこで「Thread」クラスの出番です。

Rubyに限らず多くのプログラミング言語では、並列処理のための仕組み(API)が用意されています。もちろんRubyも例外ではなく、並列処理の為の「Thread」クラスが用意されており、このクラスを使うことで並列処理を簡単に実装できます。

この「Thread」クラスは、このスレッドを複数作成するためのクラスで、作成した「Thread」クラスの数だけ同時に処理が実行できます。

以降はサンプルコードを作りながら「Thread」クラスの使い方を見ていきましょう。

Threadを作成する

スレッドを作るには、Thread#newを使います。また、スレッドの中で実行する処理をブロックで指定します。

次の例は、Thread#newで作成した子スレッドと、メインスレッドでそれぞれ現在時刻を出力するサンプルコードです。

th = Thread.new do
  p Time.now 
  sleep(2)
end

p Time.now

子スレッドには時刻の出力後、2秒のsleepがあるが、それぞれスレッドは並列に実行されるため、出力結果には同じ時刻が出力されます。

もし、上の処理をスレッドを使わずに(シングルスレッド)で次のように書いた場合、当然ですが、出力結果の時刻は2秒遅れで出力されます。

p Time.now 
sleep(2)
p Time.now
2021-05-12 14:02:48.386885916 +0000
2021-05-12 14:02:50.387974976 +0000

Thread#joinでスレッドの終了を待ち合わせる

スレッドは基本的には並列で実行を行いますが、時には1つまたは複数のスレッドが終了するまで処理を待ち合わせケースもあります。Thread#joinメソッドを使用すると、指定したスレッドの処理が終了するまでレシーバーの処理を待機させることができます。

次の例は、スレッドthが終了するまで、th.joinの行でメインスレッドの処理を中断させるサンプルコードです。

th = Thread.new do
  puts "スレッドを開始 #{Time.now}"
  sleep(3)
  puts "スレッドを終了 #{Time.now}"
end

puts "開始 #{Time.now}"
th.join
puts "終了 #{Time.now}"
開始 2021-05-12 13:52:07 +0000
スレッドを開始 2021-05-12 13:52:07 +0000
スレッドを終了 2021-05-12 13:52:10 +0000
終了 2021-05-12 13:52:10 +0000

このように、Thread#joinメソッドを呼ぶと、そのスレッドが終了するまで、そこで処理が中断していることが分かります。

複数のスレッドの終了を待機する

複数スレッドを同時実行している時に、すべてのスレッドの終了まで待機したい場合もあるでしょう。

1つ1つのスレッドに対してThread#joinメソッドを呼ぶコード書いてもよいですが、次のように煩雑になり、スレッド数が今後増えるとすると、保守性が悪いコードになってしまいます。

th1 = Thread.new do
  puts "スレッド1"
  sleep(3)
end

th2 = Thread.new do
  puts "スレッド2"
  sleep(1)
end

#スレッド1,2が両方完了するまで待機
th1.join
th2.join

複数のスレッドの待ち合わせを行う場合は、スレッドのオブジェクトを配列で管理し、一括で待機処理を書いた方が、コードがスッキリし将来的にもメンテナンス性が高くなります。

threads = []

threads << Thread.new do
  puts "スレッド1"
  sleep(3)
end

threads << Thread.new do
  puts "スレッド2"
  sleep(1)
end

#eachメソッドを使って配列の中のスレッドが全て完了するまで待機
threads.each{|th| th.join()}

また、eachメソッドの呼び出し部分は、シンボルにして次のように書き換えることもできます。

threads.each(&:join)

Mutexでスレッド間の排他制御

Threadクラスで並列で動く処理を作るときに注意が必要なのが、共有リソースへの同時アクセス制御である。

次の例は、変数countをスレッドの中でインクリメントしていき、1〜10までの合計値を求めるコードです。

count = 0
sum = 0

threads = [*1..10].map do |i|
  Thread.new do
    if flag
      count += 1
      flag = false
    end
  end
end

threads.each(&:join)
p count

本来であれば1〜10までの合計は55であるが、並列でスレッドの処理が動き、countをインクリメントしたことにより、一気にcountの数値が上がってしまい合計値がおかしくなってしまいました。

このように、複数のスレッドから同時にアクセスされると問題になる変数などの共有リソースを扱う場合は、排他制御を用います。

RubyにはThread::Mutexクラスが用意されており、一般的にはこのクラスを使っててスレッド間の排他制御を行います。

Thread::Mutexクラスで排他制御することで、並列処理であっても共有リソースにアクセスする処理の時だは、シーケンシャルに処理を行い、共有リソースへの同時アクセスを防げます。

では、先程の1〜10までの合計値をThread::Mutexを使って排他制御してみましょう。

count = 0
sum = 0

threads = [*1..10].map do |i|
  Thread.new do
    if flag
      count += 1
      flag = false
    end
  end
end

threads.each(&:join)
p count

今度は正しく1〜10までの合計値として「55」が表示されました。

ポテパンダの一言メモ

Thread::Mutexクラスを用いることで、並列処理の中で、一部の処理だけは同時に実行されないような制御ができます。

まとめ

Rubyの「Thread」クラスを使って、複数のスレッドを作成し、並列処理を実装する方法を解説してきました。

マルチスレッドによるプログラミングは、上から下へ逐次処理を行っていく、シングルスレッドのプログラミングに比べ、気をつけるべき点や、処理の複雑性が一気に上がります。

すぐに「Thread」クラスを使って効率的で安全な並列処理処理を作ることハードルが高いかもしれませんが、色んな処理をスレッドで作成してマルチスレッドマスターを目指していきましょう。

【関連記事】
RubyでQueue(キュー)プログラミング!キューとスタック概要から丁寧に解説

エンジニアになりたい人に選ばれるプログラミングスクール「ポテパンキャンプ 」

ポテパンキャンプは卒業生の多くがWebエンジニアとして活躍している実践型プログラミングスクールです。 1000名以上が受講しており、その多くが上場企業、ベンチャー企業のWebエンジニアとして活躍しています。

基礎的な学習だけで満足せず、実際にプログラミングを覚えて実践で使えるレベルまで学習したいという方に人気です。 プログラミングを学習し実践で使うには様々な要素が必要です。

それがマルっと詰まっているポテパンキャンプでプログラミングを学習してみませんか?

卒業生の多くがWebエンジニアとして活躍

卒業生の多くがWeb企業で活躍しております。
実践的なカリキュラムをこなしているからこそ現場でも戦力となっております。
活躍する卒業生のインタビューもございますので是非御覧ください。

経験豊富なエンジニア陣が直接指導

実践的なカリキュラムと経験豊富なエンジニアが直接指導にあたります。
有名企業のエンジニアも多数在籍し品質高いWebアプリケーションを作れるようサポートします。

満足度高くコスパの高いプログラミングスクール「ポテパンキャンプ」

運営する株式会社ポテパンは10,000人以上のエンジニアのキャリアサポートを行ってきております。
そのノウハウを活かして実践的なカリキュラムを随時アップデートしております。

代表の宮崎もプログラミングを覚えサイトを作りポテパンを創業しました。
本気でプログラミングを身につけたいという方にコスパ良く受講していただきたいと思っておりますので、気になる方はぜひスクール詳細をのぞいてくださいませ。