NAME

threads::shared - スレッド間でデータ構造を共有するための Perl エクステンション

VERSION

このドキュメントは threads::shared バージョン 1.14 を記述しています。

SYNOPSIS

  use threads;
  use threads::shared;

  my $var :shared;
  $var = $scalar_value;
  $var = $shared_ref_value;
  $var = share($simple_unshared_ref_value);

  my ($scalar, @array, %hash);
  share($scalar);
  share(@array);
  share(%hash);
  my $bar = &share([]);
  $hash{bar} = &share({});

  { lock(%hash); ...  }

  cond_wait($scalar);
  cond_timedwait($scalar, time() + 30);
  cond_broadcast(@array);
  cond_signal(%hash);

  my $lockvar :shared;
  # condition var != lock var
  cond_wait($var, $lockvar);
  cond_timedwait($var, time()+30, $lockvar);

DESCRIPTION

デフォルトにおいて、変数は各スレッドに対しプライベートで、 新たに生成されたスレッドはすでに存在している各変数のプライベートなコピーを 得ます。 このモジュールは、異なるスレッド(と Win32 上の擬似 fork)間で変数を 共有することを可能にします。 threads モジュールと共に使います。

エクスポート

share, cond_wait, cond_timedwait, cond_signal, cond_broadcast, is_shared

まだ threads がロードされていない段階でこのモジュールをインポートすると、 その機能は全て無効になることに注意してください。 これによりスレッド・非スレッド環境の両方で動作するモジュールを 書くことができます。

関数

share VARIABLE

share は値を引数に取り、それを共有化されたものとしてマークします。 スカラー、配列、ハッシュ、スカラーリファレンス、配列リファレンス、あるいは ハッシュリファレンスを共有化することができます。 share は共有化された右辺値(rvalue)を返しますが、それは常に リファレンスとして返されます。

:shared 属性( my $var : shared; )を使うことで、ある変数を コンパイル時に共有化されたものとしてマークすることもできます。

Perl のプロトタイプ宣言に伴う問題で、もし新たに生成したリファレンスを 共有したい場合、&share([])&share({}) という構文を 使う必要があります。

共有したスカラに代入できる値は、他のスカラ値か、共有した リファレンスだけです:

  my $var :shared;
  $var = 1;              # ok
  $var = [];             # error
  $var = &share([]);     # ok

share はリファレンスを正確に 1 レベルだけ調べます。 share(\$a)share($a) と等価ですが、share(\\$a) は違います。 これは、ネストした共有データ構造は、まずそれぞれの共有葉オブジェクトを作成し、 それからそれらを共有ハッシュやアレイに追加することによって 作成しなければならないことを意味します。

  my %hash :shared;
  $hash{'meaning'} = &share([]);
  $hash{'meaning'}[0] = &share({});
  $hash{'meaning'}[0]{'life'} = 42;
is_shared VARIABLE

is_shared は、指定された変数が共有されているかどうかをチェックします。 共有されていれば、(refaddr() と同様に) 変数の 内部 ID を返します。 そうでなければ、undef を返します。

  if (is_shared($var)) {
      print("\$var is shared\n");
  } else {
      print("\$var is not shared\n");
  }
lock VARIABLE

lock はスコープから外れるまで変数をロックします。 もし他のスレッドによってその変数がロックされているなら、ロックが 可能になるまで lock の呼び出しはブロックされます。 動的にネストしたスコープの中から同じスレッドによって 複数回 lock を呼び出しても安全です -- 最も外側のロックがスコープから 抜けるまでその変数はロックされ続けます。

ハッシュや配列といったコンテナオブジェクトがロックしても、 その要素はロックされません。 例えば、あるスレッドが lock(@a) するとしても、他のスレッドの行う lock($a[12]) はブロックされません。

lock は正確に 1 レベルリファレンスを辿ります。 lock(\$a) は、 lock($a) と等価ですが、lock(\\$a) とは 等価ではありません。

明示的に変数を unlock することはできないことに注意してください; ロックがスコープを抜けるのを待つしかありません。 これはブロックの内側で変数をロックすることで最も簡単に達成できます。

  my $var :shared;
  {
      lock($var);
      # $var is locked from here to the end of the block
      ...
  }
  # $var is now unlocked

もし共有変数へのアクセスについてより精度の高い制御を望むなら、 Thread::Semaphore を参照してください。

cond_wait VARIABLE
cond_wait CONDVAR, LOCKVAR

cond_wait 関数は ロックされた 変数を引数に取り、その変数のロックを 解除します。 そして他のスレッドがその同じロックされていた変数に向けて cond_signalcond_broadcast するまで、ブロック(待機)します。 cond_wait がブロックする変数は、cond_wait が完了した後、再度 ロックされます。 もし複数のスレッドが同じ変数に対して cond_wait しているなら、 一つを除いて全てのスレッドがロックを獲得するまで待機するために再度 ブロックします (よって同期のために cond_wait を使うだけなら、可能な限り 早くロックを解除してください)。 変数のロック解除と、ブロックされて待ち状態に入るという二つの動作は アトミックです。 待ち状態から抜けることと、変数の再ロックという二つの動作は、 アトミックではありません。

第二の書式では、cond_waitロックされていない 共有変数をとり、 その後ろに ロック された共有変数がきます。 この二番目の変数はロックが解除され、そして他のスレッドが一番目の変数に シグナルを送るまで、そのスレッドの実行は停止します。

どのスレッドも、変数に対し cond_signalcond_broadcast をしなくても、 その変数は notify されうるということに注意することが重要です。 それゆえ、変数の値のチェック及び、要求が満たされない場合に待ち状態へ 戻ることが重要です。 例えば、共有カウンタが 0 になるまで停止するには:

  { lock($counter); cond_wait($count) until $counter == 0; }
cond_timedwait VARIABLE, ABS_TIMEOUT
cond_timedwait CONDVAR, ABS_TIMEOUT, LOCKVAR

二つの引数をとる形式では、cond_timedwaitロックされた 変数と 絶対的なタイムアウト時間を引数にとります。 変数はロック解除され、タイムアウト時間に達するか、他のスレッドが変数に シグナルを送るかするまでブロックされます。 タイムアウトになると偽の値が返されます。 そうでなければ真の値が返されます。 どちらの場合でも戻りの際に変数は再ロックされます。

cond_wait 同様、この関数は ロックされた 共有変数を追加の パラメータとしてとれます; この場合、最初のパラメータは ロックされていない 条件変数であり、これと区別されるロック変数によって 守られます。

さらに cond_wait 同様、覚醒とロックの再獲得はアトミックではありません。 この関数が戻った後、あなたが望んだ状態になっているかどうか常に チェックするべきです。 しかし、タイムアウトは絶対的な値なので、パスごとに 再計算させるべきではありません:

  lock($var);
  my $abs = time() + 15;
  until ($ok = desired_condition($var)) {
      last if !cond_timedwait($var, $abs);
  }
  # we got it if $ok, otherwise we timed out!
cond_signal VARIABLE

cond_signal 関数は ロックされた 変数を引数にとり、その変数に対して cond_wait している一つのスレッドのブロックを解除します。 もし一つ以上のスレッドが cond_wait してブロックされているなら、 ただ一つのスレッドだけがブロックを解除されます (そしてどの一つかは 不確定です)。

もしどのスレッドもその変数を cond_wait していない場合、シグナルは 破棄されます。 常にシグナルの前にロックされるので、他のスレッドが cond_wait() に 入る前にシグナルを発するのを(注意深くやれば)回避することが出来ます。

ロックされていない変数に対し cond_signal を試みると、通常は警告を 発します。 稀に起こるこの警告がうるさい場合、次の方法で警告を抑制することができます:

  { no warnings 'threads'; cond_signal($foo); }
cond_broadcast VARIABLE

cond_broadcast 関数は cond_signal とよく似た働きをします。 しかし cond_broadcast はスレッド一つだけではなく、ロックされた変数に 対して cond_wait して待機中の 全ての スレッドをブロック解除します。

OBJECTS

threads::shared bless がスレッドをまたいで伝搬するような 共有オブジェクトで動作するバージョンの bless() を エクスポートします。

  # Create a shared 'foo' object
  my $foo;
  share($foo);
  $foo = &share({});
  bless($foo, 'foo');

  # Create a shared 'bar' object
  my $bar;
  share($bar);
  $bar = &share({});
  bless($bar, 'bar');

  # Put 'bar' inside 'foo'
  $foo->{'bar'} = $bar;

  # Rebless the objects via a thread
  threads->create(sub {
      # Rebless the outer object
      bless($foo, 'yin');

      # Cannot directly rebless the inner object
      #bless($foo->{'bar'}, 'yang');

      # Retrieve and rebless the inner object
      my $obj = $foo->{'bar'};
      bless($obj, 'yang');
      $foo->{'bar'} = $obj;

  })->join();

  print(ref($foo),          "\n");    # Prints 'yin'
  print(ref($foo->{'bar'}), "\n");    # Prints 'yang'
  print(ref($bar),          "\n");    # Also prints 'yang'

注意

threads が利用できない場合、threads::shared は黙って利用不可になるよう 設計されています。 threads にアクセスしようとするなら、use threads::shared する前に use threads しなければなりません。 threads::shared の後に threads を use しようとすれば、 警告が発せられます。

バグと制限

share が配列、ハッシュ、配列リファレンス、ハッシュリファレンスで 使われると、含まれるあらゆるデータは失われます。

  my @arr = qw(foo bar baz);
  share(@arr);
  # @arr is now empty (i.e., == ());

  # Create a 'foo' object
  my $foo = { 'data' => 99 };
  bless($foo, 'foo');

  # Share the object
  share($foo);        # Contents are now wiped out
  print("ERROR: \$foo is empty\n")
      if (! exists($foo->{'data'}));

従って、このような変数は、共有すると宣言した 後に 設定してください。 (スカラとスカラリファレンスはこの問題の影響を受けません。)

クラス自身が共有に対応するように書かれていないオブジェクトを共有することは たいてい賢明なこととは言えません。 例えば、各スレッドのスコープから外れる毎に、あるオブジェクトの デストラクタが複数回呼び出されるかも知れません。 もう一つの危険性は、ハッシュベースのオブジェクトの内容が、 上述した制限のために失われてしまうことです。 オブジェクト共有に対応したクラスの作り方については、(このモジュールの CPAN 配布に含まれている examples/class.pl を参照してください。

配列に対する splice はサポートしていません!

共有化された配列とハッシュの要素へのリファレンスをとっても 自動有効化しません。 また、共有配列/ハッシュで存在しないインデックスや キーにスライスしても、その要素は自動有効化しません。

share($hashref->{key}) しても、share() はエラーメッセージを 出しません。 しかし、$hashref->{key} は共有 されませんlock($hashref->{key}) しようとすれば "locking can only be used on shared values" (ロックは共有変数に対してのみ使用できます) というエラーが発生します。

既に報告されているバグの閲覧や、新しいバグ、問題、パッチなどの投稿先: http://rt.cpan.org/NoAuth/Bugs.html?Dist=threads-shared

SEE ALSO

CPAN の threads::shared ディスカッションフォーラム: http://www.cpanforum.com/dist/threads-shared

threads::shared の注釈付き POD: http://annocpan.org/~JDHEDDEN/threads-shared-1.14/shared.pm

ソースレポジトリ: http://code.google.com/p/threads-shared/

threads, perlthrtut

http://www.perl.com/pub/a/2002/06/11/threads.htmlhttp://www.perl.com/pub/a/2002/09/04/threads.html

Perl threads メーリングリスト: http://lists.cpan.org/showlist.cgi?name=iThreads

AUTHOR

Artur Bergman <sky AT crucially DOT net>

threads::shared is released under the same license as Perl.

Documentation borrowed from the old Thread.pm.

CPAN version produced by Jerry D. Hedden <jdhedden AT cpan DOT org>.