299

グリーンスレッドを使用したルビーの「協調的」スレッドについて知っています。処理に複数の CPU コアを利用するために、アプリケーションで実際の「OS レベル」のスレッドを作成するにはどうすればよいですか?

4

9 に答える 9

615

Jörg の 2011 年 9 月のコメントで更新

ここでは、Ruby プログラミング言語と、Ruby プログラミング言語の特定の実装の特定のスレッド モデルという2 つの非常に異なるものを混同しているようです。現在、Ruby プログラミング言語には約 11 の異なる実装があり、非常に異なる独自のスレッド モデルを備えています。

(残念ながら、これらの 11 の実装のうち実際に本番環境で使用できるのは 2 つだけですが、年末までにはその数はおそらく 4 つまたは 5 つに増えるでしょう。) (更新: 現在は 5 つです: MRI、JRuby、YARV (インタープリター) Ruby 1.9 用)、Rubinius および IronRuby)。

  1. 最初の実装には実際には名前がありません。そのため、参照するのが非常に厄介であり、本当に煩わしく、混乱を招きます。Ruby プログラミング言語の機能と特定の Ruby 実装との間で際限のない混乱を招くため、名前がないよりもさらに厄介で混乱を招くのは、ほとんどの場合「Ruby」と呼ばれます。

    「MRI」(「Matz の Ruby 実装」)、CRuby、または MatzRuby と呼ばれることもあります。

    MRI は Ruby Threads をそのインタープリター内の Green Threads として実装します。残念ながら、これらのスレッドを並行してスケジュールすることはできません。一度に 1 つのスレッドしか実行できません。

    ただし、任意の数の C スレッド (POSIX スレッドなど) を Ruby スレッドと並行して実行できるため、外部 C ライブラリ、または独自のスレッドを作成する MRI C 拡張機能を並行して実行できます。

  2. 2 つ目の実装はYARV (「Yet Another Ruby VM」の略) です。YARV は Ruby スレッドを POSIX または Windows NT スレッドとして実装しますが、グローバル インタープリター ロック (GIL) を使用して、一度に 1 つの Ruby スレッドのみを実際にスケジュールできるようにします。

    MRI と同様に、C スレッド実際には Ruby スレッドと並行して実行できます。

    将来的には、GILより細かいロックに分割され、より多くのコードが実際に並行して実行できるようになる可能性がありますが、それはまだ先のことであり、まだ計画されていません。

  3. JRuby は Ruby Threads を Native Threads として実装します。JVM の場合の「Native Threads」は明らかに「JVM Threads」を意味します。JRuby はそれらに追加のロックを課しません。したがって、これらのスレッドが実際に並行して実行できるかどうかは、JVM によって異なります。JVM スレッドを OS スレッドとして実装するものもあれば、グリーン スレッドとして実装するものもあります。(Sun/Oracle の主流の JVM は、JDK 1.3 以降、OS スレッドのみを使用します)

  4. XRubyは、Ruby スレッドを JVM スレッドとして実装しています。更新: XRuby は終了しました。

  5. IronRuby は Ruby Threads を Native Threads として実装します。ここで、CLR の場合の「Native Threads」は明らかに「CLR Threads」を意味します。IronRuby はそれらに追加のロックを課さないため、CLR がサポートしている限り、並列で実行する必要があります。

  6. Ruby.NETはRuby Threads を CLR Threads としても実装します。更新: Ruby.NET は終了しました。

  7. Rubinius は、仮想マシン内で Ruby スレッドをグリーン スレッドとして実装しています。より正確には: Rubinius VM は、「Task」と呼ばれる非常に軽量で非常に柔軟な同時実行/並列処理/非ローカル制御フロー コンストラクトと、他のすべてのコンカレンシー コンストラクト (このディスカッションのスレッドだけでなく、ContinuationsActorsなども含む) をエクスポートします。 ) は、タスクを使用して純粋な Ruby で実装されます。

    Rubinius は (現在) スレッドを並行してスケジュールすることはできませんが、それはそれほど大きな問題ではありません。Rubinius はすでに、1 つの Rubinius プロセス内で複数の POSIX スレッドで複数の VM インスタンスを並行して実行できます。スレッドは実際には Ruby で実装されているため、他の Ruby オブジェクトと同様に、シリアル化して別の POSIX スレッド内の別の VM に送信できます。(これは、BEAM Erlang VM が SMP 同時実行に使用するモデルと同じです。これは、すでにRubinius Actors に実装されています。)

    更新: この回答の Rubinius に関する情報は、もう存在しない Shotgun VM に関するものです。「新しい」C++ VM は、複数の VM にわたってスケジュールされたグリーン スレッド (つまり、Erlang/BEAM スタイル) を使用せず、複数のネイティブ OS スレッド モデルを備えた、より伝統的な単一の VM を使用します。 、およびほぼすべての JVM です。

  8. MacRubyは、Objective-C ランタイムと CoreFoundation および Cocoa フレームワークの上にある YARV のポートとして始まりました。現在は YARV とは大きく異なりますが、現在でも同じスレッド モデルを YARV と共有しています。 更新: MacRuby は、非推奨と宣言されている Apple ガベージ コレクターに依存しており、MacOSX の以降のバージョンでは削除される予定です。MacRuby はアンデッドです。

  9. Cardinalは、 Parrot Virtual Machineの Ruby 実装です。スレッドはまだ実装されていませんが、実装された場合はおそらくParrot Threadsとして実装されるでしょう。更新: カーディナルは非常に不活発/死んでいるようです。

  10. MagLevは、 GemStone/S Smalltalk VMの Ruby 実装です。GemStone/S がどのスレッド モデルを使用しているか、MagLev がどのスレッド モデルを使用しているか、さらにはスレッドがまだ実装されているかどうか (おそらく実装されていない) についても、私は知りません。

  11. HotRubyは、独自の完全な Ruby 実装ではありません。これは、JavaScript での YARV バイトコード VM の実装です。HotRuby はスレッドをサポートしていません (まだ?)。サポートされたとしても、JavaScript は真の並列処理をサポートしていないため、スレッドを並列で実行することはできません。ただし、HotRuby の ActionScript バージョンがあり、ActionScript は実際に並列処理をサポートしている可能性があります。更新: HotRuby は終了しました。

残念ながら、これら 11 の Ruby 実装のうち、実際に本番環境に対応しているのは MRI と JRuby の 2 つだけです。

したがって、真の並列スレッドが必要な場合は、JRuby が現在唯一の選択肢です。それが悪いというわけではありません。JRuby は実際には MRI よりも高速であり、間違いなくより安定しています。

それ以外の場合、「古典的な」Ruby ソリューションは、スレッドの代わりにプロセスを並列処理に使用することです。Ruby Core Library には、別の Ruby プロセスを非常に簡単にフォークできる メソッドを備えたProcessモジュールが含まれています。また、Ruby 標準ライブラリには Distributed Ruby (dRuby / dRb)ライブラリが含まれており、Ruby コードを同じマシン上だけでなくネットワーク上でも複数のプロセスに簡単に分散できます。Process.fork

于 2008-09-11T22:25:12.903 に答える
28

Ruby 1.8 にはグリーン スレッドしかありません。実際の「OS レベル」のスレッドを作成する方法はありません。しかし、Ruby 1.9 にはファイバーと呼ばれる新しい機能が追加され、実際の OS レベルのスレッドを作成できるようになります。残念ながら、Ruby 1.9 はまだベータ版であり、数か月後には安定する予定です。

もう 1 つの方法は、JRuby を使用することです。JRuby はスレッドを OS レベルのスレッドとして実装し、「グリーン スレッド」はありません。JRuby の最新バージョンは 1.1.4 で、Ruby 1.8 と同等です。

于 2008-09-11T09:05:37.833 に答える
8

それは実装に依存します:

  • MRIにはありませんが、YARVの方が近いです。
  • JRuby と MacRuby は持っています。




Ruby には、および のようなクロージャーがあります。JRuby のクロージャーと複数のコアを最大限に活用するには、Java のエグゼキューターが便利です。MacRuby の場合、 GCD の queuesが好きです。 実際の「OS レベル」のスレッド を作成できるからといって、並列処理に複数の CPU コアを使用できるわけではないことに注意してください。以下の例を見てください。BlockslambdasProcs

これは、Ruby 2.1.0 を使用して 3 つのスレッドを使用する単純な Ruby プログラムの出力です。

(jalcazar@mac ~)$ ps -M 69877
USER     PID   TT   %CPU STAT PRI     STIME     UTIME COMMAND
jalcazar 69877 s002    0.0 S    31T   0:00.01   0:00.04 /Users/jalcazar/.rvm/rubies/ruby-2.1.0/bin/ruby threads.rb
   69877         0.0 S    31T   0:00.01   0:00.00 
   69877        33.4 S    31T   0:00.01   0:08.73 
   69877        43.1 S    31T   0:00.01   0:08.73 
   69877        22.8 R    31T   0:00.01   0:08.65 

ここでわかるように、4 つの OS スレッドがありますが、状態を持つスレッドのみRが実行されています。これは、Ruby のスレッドの実装方法の制限によるものです。



JRuby を使用した同じプログラム。state の 3 つのスレッドが表示Rされます。これは、それらが並行して実行されていることを意味します。

(jalcazar@mac ~)$ ps -M 72286
USER     PID   TT   %CPU STAT PRI     STIME     UTIME COMMAND
jalcazar 72286 s002    0.0 S    31T   0:00.01   0:00.01 /Library/Java/JavaVirtualMachines/jdk1.7.0_25.jdk/Contents/Home/bin/java -Djdk.home= -Djruby.home=/Users/jalcazar/.rvm/rubies/jruby-1.7.10 -Djruby.script=jruby -Djruby.shell=/bin/sh -Djffi.boot.library.path=/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jni:/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jni/Darwin -Xss2048k -Dsun.java.command=org.jruby.Main -cp  -Xbootclasspath/a:/Users/jalcazar/.rvm/rubies/jruby-1.7.10/lib/jruby.jar -Xmx1924M -XX:PermSize=992m -Dfile.encoding=UTF-8 org/jruby/Main threads.rb
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    33T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.09   0:02.34 
   72286         7.9 S    31T   0:00.15   0:04.63 
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.04   0:01.68 
   72286         0.0 S    31T   0:00.03   0:01.54 
   72286         0.0 S    31T   0:00.00   0:00.00 
   72286         0.0 S    31T   0:00.01   0:00.01 
   72286         0.0 S    31T   0:00.00   0:00.01 
   72286         0.0 S    31T   0:00.00   0:00.03 
   72286        74.2 R    31T   0:09.21   0:37.73 
   72286        72.4 R    31T   0:09.24   0:37.71 
   72286        74.7 R    31T   0:09.24   0:37.80 


同じプログラムが MacRuby に。また、並行して実行される 3 つのスレッドもあります。これは、MacRuby スレッドが POSIX スレッド(実際の「OS レベル」のスレッド) であり、GVL がないためです。

(jalcazar@mac ~)$ ps -M 38293
USER     PID   TT   %CPU STAT PRI     STIME     UTIME COMMAND
jalcazar 38293 s002    0.0 R     0T   0:00.02   0:00.10 /Users/jalcazar/.rvm/rubies/macruby-0.12/usr/bin/macruby threads.rb
   38293         0.0 S    33T   0:00.00   0:00.00 
   38293       100.0 R    31T   0:00.04   0:21.92 
   38293       100.0 R    31T   0:00.04   0:21.95 
   38293       100.0 R    31T   0:00.04   0:21.99 


もう一度、同じプログラムですが、今は古き良き MRI を使用しています。この実装はグリーン スレッドを使用するため、スレッドは 1 つしか表示されません。

(jalcazar@mac ~)$ ps -M 70032
USER     PID   TT   %CPU STAT PRI     STIME     UTIME COMMAND
jalcazar 70032 s002  100.0 R    31T   0:00.08   0:26.62 /Users/jalcazar/.rvm/rubies/ruby-1.8.7-p374/bin/ruby threads.rb



Ruby のマルチスレッド化に興味がある場合は、フォーク ハンドラーを使用した並列プログラムのデバッグというレポートが興味深いものになるかもしれません。
Ruby 内部のより一般的な概要については、Ruby Under a Microscopeをお読みください。
また、Ruby Threads and the Global Interpreter Lock in C in Omniref では、Ruby スレッドが並列で実行されない理由をソース コードで説明しています。

于 2014-02-03T16:00:41.660 に答える
4

drbの使用はどうですか?これは実際のマルチスレッドではなく、複数のプロセス間の通信ですが、1.8 で使用できるようになり、かなり摩擦が少なくなりました。

于 2008-09-11T11:57:27.797 に答える
1

Ruby で製品レベルのシステム (ベータ版を使用できない) で並列処理が本当に必要な場合は、おそらくプロセスの方が優れた代替手段です。
しかし、最初に JRuby の下でスレッドを試す価値は間違いなくあります。

また、Ruby での将来のスレッド化に興味がある場合は、この記事が役に立つかもしれません。

于 2008-09-11T19:10:36.257 に答える
1

MRI を使用している場合は、拡張機能として、または ruby​​-inline gem を使用して、スレッド コードを C で記述できます。

于 2008-09-11T18:50:51.407 に答える
1

これは、Linda の Ruby 実装 (並列処理と分散コンピューティングのパラダイム) である Rinda に関する情報です。

于 2010-01-01T00:24:29.403 に答える