OK、共有変数へのリファレンスだ。section.共有変数が、複数のスレッドが同時にその変数に変更を加えようとしても、変数の内部状態は破壊されないことを保証していることに注意。次のセクションで説明するように、ここを超えてしまうと保証はなくなってしまう。condition:スレッドは新しい便利なツールを一揃いもたらしてくれる一方で、たくさんの落とし穴ももたらす。その一つは競合条件である。なるだろうか?残念ながら、その答えは”場合によりけり”だ。あなたの使っているスレッドが実装するスケジューリングアルゴリズムから月の満ち欠けまでの種々の要因によって、$aは2にも3にもなりうる。error:競合条件は共有データに対して非同期なアクセスを行うことによって生じる。明示的に同期をとらない限り、アクセスしてアップデートするまでの間に共有データに何も起こらないことを確実にする方法はない。こんな単純なコードでさえ、エラーの可能性があるのだ。それぞれのスレッドはいずれかの時点で割り込まれたり、いずれかの順番で実行される可能性がある。ともに2か3になるだろう。5や$a++でさえ、アトミックな演算であることを保証できない。you.他のスレッドによってアクセスされる可能性のあるデータやリソースにあなたのプログラムがアクセスするときはいつでも、整合的なアクセスのための手順を踏まなければならない。なければデータの一貫性が崩れたり、競合条件に陥るリスクを負うことになる。involved.競合条件やそれに似た状態を回避するために、スレッドとデータとの間の相互作用を調整する多くのメカニズムをPerlは提供する。ようなスレッドライブラリにおいて使われる共通のテクニックに似た設計がなされている。あるものはPerlに特化している。標準的なテクニックというものはぎこちないものであり、正しく扱うには難しい。可能なところでは通常、queuesのようなPerl的テクニックはより簡単であり、難しい仕事のうちのあるものを取り除いてくれる。total:lock()関数は共有変数を引数にとり、それにロックをかける。ロックを保持するスレッドによって変数のロックが解除されるまで、他のスレッドはロックをかけることができないだろう。関数を含むブロックの外に出るとロックは自動的に解除される。lock()の利用は率直なやり方だ。この例は、ある計算を並列的に処理し、時折その総計値を更新するいくつかのスレッドを伴っている。exits.ロックされてる変数が利用可能になるまで、lock()はそのスレッドをブロックする。lock()が戻ってくると、そのロックが存在しているブロックの一番外に出ない限り、他のスレッドがその変数をロックできないことが確実になる。you.ロックは問題の変数にアクセスするのを阻止するのではなくて、ロックの試みを阻止するのだということに注意することが重要だ。これはPerlの長年にわたる作法どおりのプログラミングの伝統と、flock()が提供する勧告ロックと調和するものである。itself.スカラー変数同様、配列やハッシュにもロックをかけることができるだろう。配列へのロックは、配列の要素に対する二次的なロックをブロックするのではなく、配列そのものへのロックの試みをブロックする。example:ロックは再帰的、つまりスレッドはある変数に対し何度もロックをかけて大丈夫だ。外側のlock()がスコープを抜けるまでロックは続く。scope.unlock()関数は無いことに注意。変数のロックを解除する方法はスコープをぬけさせることだけだ。libraries.ロックされている変数内に保持されているデータをガードするか、あるいはコードのセクションのようなものを守るために、ロックは用いられる。後者の場合、問題の変数は役に立つデータを持たず、ロックの目的のためにだけ存在する。この点からすると、その変数は伝統的なスレッドライブラリにおけるミューテックスや基本的なセマフォのような振る舞いをするといえる。code:ロックはデータに同期アクセスするための便利なツールである。適切に使えば安全な共有データへの鍵となる。残念なことに、ロックには危険が伴う。複数のロックが関わるとそうなる。次のコードを考えてみよう。するまで固まってしまうだろう。ハングさせないための唯一の方法は、どちらかのスレッドが先に両方のロックを獲得することだ。ハングを保証するバージョンはもっと複雑だが、原理はこれと同じだ。ロックを手に入れようと試みる。ロックを手に入れようとする。それぞれのスレッドが、他方のスレッドのロックが開放されるの待つため、二番目のロックへの試みはブロックしてしまう。released.この状態をデッドロックという。二つ以上のスレッドが他スレッドの所有するリソースをロックしようと試みるときにはいつでも生じる。他のスレッドがリソースへのロックを開放するのを待って、互いにスレッドがブロックする。リソースを持っているスレッドが自分自身のロックの開放を待つ場合は、は生じない。deadlock.この種の問題を扱う方法はたくさんある。よい方法は、常に全てのスレッドが正確に同じ順番でロックを獲得するようにさせることだ。ロックするようにする。デッドロックの危険を最小にするために、短い時間だけロックを保持するのも、良い手だ。problems.下記で説明するような他のプリミティブな同期も同様な問題を抱えうる。this:キューとは、一方の端にデータを入れ、他方から取り出すことによって、同期の問題を心配しなくてすむ、特別なスレッドセーフオブジェクトである。これらは非所に素朴で、こんな感じだ。Thread::Queueでキューを生成する。使ってキューの最後にスカラーのリストを追加する。キューの前の方からスカラーを取り出す。キューのサイズは固定していない。キューの中に押し込められたものを保持する必要に応じてサイズは成長する。threads.もしキューが空っぽの場合、他のスレッドが何かをキューの中に入れるまで、dequeue()はブロックする。time.セマフォは包括的なロックメカニズムの一種である。基本となる形態では、セマフォはデータを持つことはできないし、明示的にロック解除されなければならないことを除くと、ロック可能なスカラーそっくりに振舞う。発展的な形態においては、一種のカウンターのように動作し、いつでも複数のスレッドが”ロック”を持つことを可能にする。demonstration:セマフォは二つのメソッド、down()とup()を持つ。down()の呼び出しはブロックする。このプログラムは短い例だ。once.三つのサブルーチンは同時に作用する。一度に一つのスレッドだけがグローバル変数にアクセスすることをセマフォが保証する。semaphores.デフォルトでは、セマフォはロックのように振舞う。セマフォには別の使い方がある。values:それぞれのセマフォは、関連付けられたカウンタを持つ。デフォルトでセマフォは生成時、カウンタに1がセットされる。down()はカウンタから1を引き、up()は1を足す。この規定値の一部ないしは全部を別の値でもって上書きすることができる。$semaphore->down(1).もしもdown()がカウンタを0より小さい値に下げようとするなら、down()はカウンタが十分な大きさになるまでブロックする。セマフォはカウンタの値を0にして生成することができる一方、up()やdown()は常に、少なくとも1の変化をカウンタに対して行うことに注意すること。once.もちろん、問題はどうしてこんなことをするのかということだ。セマフォを1ではなくて、0から始めるように生成するのか?あるいは、なぜ1以上の値で増減を行うのか?その答えはリソースアベイラビリティだ。あなたがアクセス管理をしたい多くのリソースは、同時に一つ以上のスレッドによって安全に利用される。drawing.例としてGUI駆動型のプログラムを取り上げてみよう。プログラムは、ディスプレイに同期アクセスするためにセマフォを持っている。して一度にただ一つのスレッドだけが描画を行っている。簡単な話だが、もちろん、ちゃんと準備ができるまでどのスレッドにも描画を始めてもらいたくはない。こんな場合に、カウンタを0にしてセマフォを生成するのだ。描画の準備ができたら、カウンタを増加させる。themselves.1より大きいカウンタを持つセマフォはクォータの確立にも役立つ。同時にI/Oを使うスレッドがいくつもあるとする。全てのスレッドが同時に読み書きをするのは望まない。そんなことをしたらI/Oチャンネルを圧迫するかもしれないし、プロセスに割り当てられたファイルハンドルを枯渇させてしまうかもしれないからだ。あなたがいつでも必要とし、スレッドにおとなしくブロック・アンブロックをさせたいだけの現I/Oリクエスト数(あるいはオープンファイル数)で初期設定されたセマフォを利用すればよい。once.あるスレッドが一度にたくさんのリソースを借りたり戻したりするようなこういうケースでは、大きな数で増減を行うのが便利だ。details.これら二つの関数は、ロックが同時発生する時に、リソースが利用可能になった協調型スレッドに通知を行うために用いる。ある関数とよく似ている。ほとんどの目的において、キューの方がより単純で直感的である。threads::sharedを参照。else.Perlのスレッドパッケージの働き部分をみてきた。これらの道具を使ってスレッドを使ったコードやパッケージを書く際にうまくやるべきだ。他の場所では実際にはうまくいかないが便利な部分もわずかだがある。クラスメソッドを使うと、現在のスレッドを表すオブジェクトを得ることができる。スレッドを生成するときに返ってくるオブジェクトと同じように、このオブジェクトを使うことができる。created.tid()はオブジェクトが表すスレッドのIDを返すメソッドだ。スレッドIDは整数で、プログラム中のメインスレッドは0である。現在のPerlは、プログラム中で生成された全てのスレッドに一意なIDをつける。最初に生成されたスレッドには1があてられ、新しいスレッドが作られる度にIDは1づつ増えていく。don't.equal()メソッドは二つのスレッドオブジェクトを引数にとり、オブジェクトが同じスレッドを示していれば真を、そうでなければ偽を返す。objects.スレッドオブジェクトは比較演算子==をオーバーロードするので、普通のオブジェクトのようにそれらを比較することもできる。program:threads->listはスレッドオブジェクトのリストを返す。detachされていないスレッドである。プログラムの終わりに掃除をするのも含めていろいろな点で便利だ。これは、他のスレッドが実行中の間はPerlが自分自身を掃除することができないためだ。threads.まだ混乱しているって?では、サンプルプログラムを使ってこれまで見てきたことのいくつかを示す時だ。このプログラムはスレッドを使って素数を見つけだす。pipeline.このプログラムは素数を発生させるためにパイプラインモデルを利用している。パイプラインにおけるそれぞれのスレッドは、素数を見つけるためにチェックされる数を渡すインプット・キューと、チェックに失敗した数をかき集めておくアウトプット・キューとを持つ。チェックに失敗した数がスレッドにあり、かつ、子スレッドがない場合、そのスレッドは新しい素数を見つけたことになる。この場合、新しい子スレッドはその素数のために生成され、パイプラインの後ろにくっつけられる。1)これは実際よりもわかりにくいかもしれない。このプログラムを部分部分にわけて、なにをやっているのかみてみよう。(素数とは正確には何っだたかということを思い出そうされている方のためにいうと、自分自身と1でのみ割り切れる数のことだ。21).作業の大半はcheck_num()ルーチンによってなされる。このルーチンはインプット・キューへのリファレンスとスレッドが受け持つ素数を引数にとる。[引数を]インプット・キューとサブルーチンがチェックする素数[のローカル変数]に入れた後(20行目)、新しいキューを生成する(22行目)。後で生成するかもしれないスレッドを入れるスカラー変数を用意する(21行目)。ループでインプット・キューからスカラー値を取り出し、このスレッドが受け持つ素数でチェックする。24行目では、チェックされる数に対する法を行い、余りがあるかどうかを調べる。あれば、その数は素数では割れないということだ。そこでその数を、すでに生成してあるのであれば次のスレッドに渡すし、そうでなければ新しいスレッドをつくる。found.29行目で新しいスレッドの生成を行っている。そのスレッドに、先につくったキューへのリファレンスと発見された素数を渡す。あるかして)ループが終了したら、子スレッドをつくていた場合には子スレッドに注意を促し[(undefを送る)]、実行が終了するのを待つ(32,33行目[原文は37])。それからチェックするための3から1000までの数字をキューに入れる(12〜14行目)。通知[undef]を入れる(16行目)。最初の子スレッドが終了するのを待つ(17行目)。戻ってきたら作業が終わったことになる。program.以上が仕組みだ。多くのPerlプログラムにとってそうであるように、説明の方が当のプログラム以上にとても長い。code.iスレッドと他のスレッドモデルを比較する際に忘れてならないのが、新しいスレッドはみな、親スレッドの変数とデータを全て完全にコピーして引き渡されるという事実だ。そのため、メモリ使用量と実行時間の両方の点で、スレッドの生成は非常に高くつく。このコストを減らす理想的な方法は、長生きなスレッドを比較的少なめに持つことだ。このスレッドは全て、ベースとなるスレッドが非常に多くのデータを蓄積する前に、適切に早い段階で生成される。これはいつも可能というわけではないだろうから、妥協も必要だ。スレッドが生成された後は、そのパフォーマンスと超過メモリー使用量は、普通のコードのそれとほとんど違いは無い。variables.また、現在の実装下では、共有変数は通常の変数に比べて少し余計にメモリを使用し、スピードも少し遅いことに注意。threads.スレッドそれ自身は別々に実行され、Perlのデータは明示的に共有化しない限りはスレッド内でプライベートなものだ。その一方、スレッドは全てのスレッドに影響を与えながら、プロセススコープの状態に影響を与えてしまうことに注意すること。changes.この最も一般的な例はchdir()を使ったときに現在作業中のディレクトリが変更されてしまうことだ。一つのスレッドがchdir()を呼ぶと、全てのスレッドの作業ディレクトリが変更される。chdir()).さらに劇的なプロセススコープの変更例はchroot()だ。全部のスレッドのルートディレクトリが変更される。スレッドはそれを元に戻すことはできない(chdir()の方は可能)。uids/gids.さらなるプロセススコープ変化の例は、umask()とuid/gidの変更である。different.)fork()とスレッドを混在させたらどうなるかって?そんな気分が失せるまで、横になって休んでいてください...。本当に知りたいなら、fork()は全てのスレッドを複製するというのが、そのセマンティクスだ(少なくともUNIXの場合では。他のプラットフォームでは違うことになるだろう)。API).同じように、シグナルとスレッドを混ぜるのもやるべきではない。APIを提供することさえできない)。state.様々なライブラリの関数呼び出しがスレッドにとって安全かどうかということは、Perlのコントロールの埒外である。一般的にいって、グローバルな外部状況に依存する呼び出しが含まれる。documentation.Perlがコンパイルされたシステムが、そういった呼び出しのスレッドセーフなバージョンを持っているなら、そちらが使われる。それを超えると、Perlは呼び出しがスレッドセーフかどうかのなすがままになる。ライブラリのドキュメントをよく吟味してほしい。reasons).プラットフォームによっては、結果を保持するバッファが小さすぎると、スレッドセーフなインタフェースが失敗することがある(例えば、getgrent()は非常に大きなグループ情報のりストを返すかもしれない)。Perlは何度かバッファを大きくするよう試みるだろうが、64kまでだ(安全上の理由から)。expert.完全なスレッドのチュートリアルをつくると一冊の本になってしまう。この導入書で見てきたことを使って、スレッドPerlのエキスパートになる道をうまく進んでいってほしい。<jwalt@cpan.org<gt>によって改訂された。Journal)にある。この文書の配布はPerlのそれと同じ条件のもとになされる。threads::shared.より詳細な情報はthreadsとthreads::sharedを参照。Association。