本記事では、Rubyで並行(concurrent)に処理を行う手段である、Threadとforkの解説を行います。
スレッド(Thread)とは
スレッド(Thread)とは、ひとつのプロセス上で複数の処理を実行させる際の処理単位です。
Ruby1.8まではOSに依存しないユーザレベルのスレッド(これをグリーンスレッドと呼びます)を使っていましたが、スレッド切り替えが頻繁に起こる際のパフォーマンス向上を目的に、1.9以降はネイティブ スレッド(= OS組み込み)を利用する実装に変更されています。
ただしここで注意すべきは、基本的にRubyのスレッドは複数のネイティブ スレッドを同時に使うことは出来ない、という点です。リファレンスマニュアルに概要が記載されているので引用します。
ネイティブ スレッドを用いて実装されていますが、 現在の実装では Ruby VM は Giant VM lock (GVL) を有しており、同時に実行される ネイティブ スレッドは常にひとつです。 ただし、IO 関連のブロックする可能性があるシステムコールを行う場合には GVL を解放します。その場合にはスレッドは同時に実行され得ます。 また拡張ライブラリから GVL を操作できるので、複数のスレッドを 同時に実行するような拡張ライブラリは作成可能です。
つまり、RubyのスレッドはIO処理(ディスクの読み書きやネットワーク通信)に関しては並列(parallel)に実行できますが、それ以外のスレッド実行は「プロセスよりも細かい時間単位でスレッドを切り替える」ことにより並列っぽく見せているだけのもので、仮にある一瞬で時間停止して見たとすればその瞬間に実行中のスレッドは一つです。
Threadの使い方
それでは実際にスレッドを使ってみましょう。
Thread.new
することで新しいスレッドが作成され、これブロックを与えるとで新スレッドでブロック内のコードが実行されることになります。以下は、スレッドを使って画像を100枚並列でダウンロードする例です。
(ちなみにCamBelt.co: The Leading Cam Belt Site on the Netはダミー画像を生成してくれるサービス)
スレッドを使わず同じタスクを行う以下のコードも用意して、
速度を比較してみます。
スレッドを使わない場合が26.136秒、スレッドを使った場合は0.123秒と大幅な改善が見られます。
次のページでは、別プロセスを生み出して処理を実行させるforkについて説明します。