Rubyで実装されたタプルスペース(Tuple Space)を扱うためのライブラリです。
タプルスペースとは並列プログラムにおける一つのパターンです。並列プログラミングにおいては、ロックのような同期処理が必須ですが、適切な同期処理を実現することは困難をともないます。このパターンにおいては、複数の並列単位(スレッド/プロセス)間の通信をすべてタプルスペースという領域を経由して行います。これによってプロセス間の通信トポロジーを単純化し、問題を簡単化します。タプルスペースに対しては、タプルを書き込む(write)、取り出す(take)、タプルの要素を覗き見る(read) という操作のみが利用できます。可能な操作を限定し、定型化することで安全な同期処理を実現します。rinda においてはタプルとは配列もしくはハッシュテーブルを意味します。タプルを取り出すときにはパターンを指定して、それにマッチしたもののみを取り出すことができます。特にタプルの第1要素最初の要素を限定することで必要なタプルのみを取り出します。
タプルスペースそのものの実装は rinda/tuplespace でなされています。このライブラリはタプルスペースへのアクセス機能等を提供します。
Rinda::TupleSpace#take や Rinda::TupleSpaceProxy#take などでは取り出したいタプルを指定するため、パターンをメソッドの引数に渡す必要があります。
パターンは配列、もしくはハッシュテーブルのいずれかです。配列によるパターンは配列によるタプルにのみ、ハッシュテーブルによるパターンはハッシュテーブルによるタプルにのみ、それぞれマッチします。
パターンが配列の場合は、長さが同じ配列タプルにのみマッチします。そしてパターン配列の各要素が対応する配列タプルの各要素にマッチする場合にパターンがタプルにマッチします。各要素に関しては以下が成立する場合にマッチします。
パターンがハッシュテーブルの場合、キーと値のペアの個数が一致し、キーの集合が一致し、それぞれのキーに対応する値がマッチする場合にパターンがタプルにマッチします。値のマッチのルールは配列の各要素に関するマッチのルールと同じです。ハッシュテーブルのキーとしては文字列のみ使えます。
この例では、rinda_ts.rb を起動したプロセスがタプルスペースを提供します。
rindas.rb はタプルスペースに書き込まれたクエリ('sum' というキーのタプル) を取り出し、それを2倍したものを応答として('ans'というキーのタプル) タプルスペースに書き込みます。
一方 rindac.rb はクエリ('sum' というキーのタプル)をタプルスペースに書き込み、その応答('ans'というキーのタプル)をタプルスペースから取り出して表示します。
例の動かしかたは以下の通りです。
# まず、rinda_ts.rb を動かす ruby rinda_ts.rb druby://localhost:40121 # rinda_ts.rb を動かしたまま、rindas.rbを動かす # 複数の rindas.rb を同時に動かしてもよい。 # 別のターミナルで: ruby rindas.rb druby://localhost:40121 # rindac.rb を動かし、クエリをタプルスペースに書き込む ruby rindac.rb druby://localhost:40121 # on rindas.rb terminal do_it(1) do_it(2) do_it(3) do_it(4) do_it(5) do_it(6) do_it(7) do_it(8) do_it(9) do_it(10) # on rindac.rb terminal [1, 2] [2, 4] [3, 6] [4, 8] [5, 10] [6, 12] [7, 14] [8, 16] [9, 18] [10, 20]
rindas.rb や rindac.rb を同時に複数動かすと、タプルスペースの並列性の問題についてのよりよい理解が得られます。例えば rindas.rb を複数動かすと、 rindac.rb からのクエリを複数の rindas.rb が分散して処理します。複数の rindac.rb を動かしても、応答が混ざったりせず、rindac.rb に適切に応答が返されます。これは DRb.uri を使うことで rindac.rb のプロセスを一意に同定しているからです。
# rinda_ts.rb require 'drb/drb' require 'rinda/tuplespace' uri = ARGV.shift DRb.start_service(uri, Rinda::TupleSpace.new) puts DRb.uri DRb.thread.join # rindas.rb require 'drb/drb' require 'rinda/rinda' def do_it(v) puts "do_it(#{v})" v + v end uri = ARGV.shift || raise("usage: #{$0} <server_uri>") DRb.start_service ts = Rinda::TupleSpaceProxy.new(DRbObject.new(nil, uri)) while true r = ts.take(['sum', nil, nil]) v = do_it(r[2]) ts.write(['ans', r[1], r[2], v]) end # rindac.rb require 'drb/drb' require 'rinda/rinda' uri = ARGV.shift || raise("usage: #{$0} <server_uri>") DRb.start_service ts = Rinda::TupleSpaceProxy.new(DRbObject.new(nil, uri)) (1..10).each do |n| ts.write(['sum', DRb.uri, n]) end (1..10).each do |n| ans = ts.take(['ans', DRb.uri, n, nil]) p [ans[2], ans[3]] end
この例は ruby の配布物の sample/drb/rinda{_ts,s,c}.rb と同じものです。
Rinda::DRbObjectTemplate | |
Rinda::SimpleRenewer | シンプルな renewer で renewer のサンプル実装です。 |
Rinda::Tuple | Tuple のためのクラスです。ユーザがこのクラスを直接使うことはありません。 |
Rinda::Template | タプルのマッチングのためのクラスです。ユーザがこのクラスを直接使うことはありません。 |
Rinda::TupleSpaceProxy | リモートの Rinda::TupleSpace オブジェクトを包むプロクシクラスです。 |
Rinda | rinda/rinda および rinda/tuplespace の名前空間を提供するモジュール。 |
Rinda::RequestCanceledError | rinda で take などのリクエストが何らかの理由でキャンセルされたことを意味する例外クラス。 |
Rinda::RequestExpiredError | rinda で take などのリクエストがタイムアウトしたことを意味する例外クラス。 |
Rinda::RindaError | rinda ライブラリの基底例外クラス |
Rinda::InvalidHashTupleKey | Rinda::TupleSpace#write などで不正なハッシュテーブル(キーが文字列でないもの)をタプルスペースに書き込もうとすると発生すると発生する例外です。 |