ithreadPerlのithreadsについてithreadsに関する日本語のサイトがあまりないので、何か適当にメモっておきます。ithreadsのニューズグループ上記の和訳についておかしな点がありましたら当サイトの管理者までご一報ください。perldocjpにcommitしていただければなお嬉しい。留意点(重要な基本事項)できるだけ最新のPerlで使おうithreadを使うときは、できるだけ最新のPerlを使うべき。5.8.0はバグが多いので使わないほうがよい(forksプラグマの項も参考)。5.8.5より前のバージョンではスレッド生成に時間がかかる場合があるので、できれば5.8.5以降を使うのがよい。最近のバージョン(5.8.7?)では、クロージャとiスレッドを使ってもPerlがクラッシュしなくなったとか。以下の例であってるのかどうかちょっと自信ありません。joined"で終了5.8.8ではjoinした後にスレッドインタプリタが解放されるようになり、いくつかのメモリリークの問題が解決されたとあります。した時のコンテキストがjoin時のコンテキストになる。......ので、joinの戻り値にリストを得ようと思ったらnewを呼び出すところでリストコンテキストにしなければなりません。変数はコピーされる変数はスレッド開始時にそのスレッド内にコピーされます。変数を使用しなくても内部的にはコピーされます。↑スレッド内に$hogeはコピーされている!スレッドアンセーフなモジュールを利用していると、これが原因でPerlがクラッシュすることも多々あります。関数もコピーされます。詳細は関連情報の『Perlのithreadsでプログラミングをする前に知っておくべきこと』参照先を。↑&testのアドレスが違う!スカラー値以外の共有化ハッシュや配列、それらのリファレンスなどをshareで共有化すると、内部の値はクリアされます。よって、まず共有化したのち、保持するデータをいれていかなければなりません。入っていない!オブジェクトの共有Perl5.8.8の段階でthreads::sharedのドキュメントを見ると、blessは共有されたリファレンスをサポートしておらず、スレッドローカルなリファレンスをblessするだけでスレッド間に伝搬しないとあります(共有リファレンスをblessできないわけではありません。Thread::Semaphorは共有リファレンスをblessしてつくられています。ある共有リファレンスをblessしても、他のスレッドで共有されているリファレンスまでblessされないということです)。"\n";が、実際にはPerl5.8.1以降、共有されたハッシュリファレンスと配列リファレンスはblessが伝搬します。0.95でスカラーリファレンスについても作用するようになりました。ところで、既存のOOなCPANモジュールなどでblessされたオブジェクトをshare()で共有化してスレッド間に渡すとどうなるかというと、たいていの場合まともに動作しません(共有化された変数に入れられるデータタイプに制約があるから)。複雑なオブジェクトはスレッド間での共有が困難です。そこで(無茶な)解決方法ですが、管理人が書いたObject::SharableをPerl5.8.1以降で使うと、オブジェクトを共有化できます。このモジュールは、受け取ったオブジェクトをシリアライズしてクラス変数のハッシュに格納し、代わりにObject::Sharableオブジェクトを返します。メソッドが呼び出されるとAUTOLOADでオリジナルのメソッドを呼び出すので透過的に使えます。メソッド呼び出しの際に元のオブジェクトの状態を再度シリアライズします。難点は、メソッドが呼び出されるたびにデシリアライズとシリアライズが発生してしまうこと等々......実験的かつ冗談的モジュールです。GLOB型のオブジェクトには対応していません。オブジェクトは二度(以上)死ぬblessされたリファレンス、すなわちオブジェクトをスレッドに渡すと、そのスレッド内でオブジェクトが破棄される毎にDESTROYメソッドが呼び出されます。Perlのマルチスレッドでは気を付けないと、オブジェクトのDESTROYメソッドが複数回呼び出される可能性があるわけです。回避方法の一つとしてはThread::Blessがあります。先に書いたObject::Sharableでは、オブジェクトが生成されたスレッドidとDESTROYが呼び出されるスレッドidを比較することで、そのオブジェクトを生成したスレッドでのみDESTROYを呼び出すようにしています。この辺りの考え方はThread::Blessを参考にしています。ところで、DESTROYメソッド内でthreads->tidすると、Perl5.8.0では正しくスレッドidを返しません。上記の方法はPerl5.8.0では使えません。5.8.1以降でお使いください(この辺はCLONEサブルーチンの場合と同じですね)。Windows上でのdetachActiveperlの5.8.3ではdetachの問題が修正されてます。以前のWindows上では、threads::detachが機能しません。そこでThread::Detach(和訳)を使って暫定的な修正を行ないます。5.8.0、5.8.1で試してみましたが、いまいちよくわかりません→どうもソースコードみると、期待通りに動かないように見えます。バグレポート出してみました......)特別なサブルーチンCLONEについてスレッドセーフなモジュールにするために。非Perlデータ(例えばXSモジュールなどで)は、自動ではスレッド間でコピーされないのでCLONEを使う。マルチスレッドにおけるrand()の使用5.8.0でのバグ、および異なるスレッド間を通じて再現性のある数列を生成するモジュールについてスレッドにおけるシグナル処理perlthrtutによれば、「シグナルとスレッドを混ぜるべきではない」ということ。実装がOS依存のためどうなるかわからない。これはPOSIXモジュールを使ってもまた然り。プロセス内であるスレッドから他のスレッドにシグナルを送るだけなら素直にcond_waitとcond_signalを使えば良いでしょう。一応Perlには標準モジュールでThread::Signalというのがありますが、これは古い5005thread用です。CPANにもithreads用のがありますが、あまり使えなさそうです。標準モジュールのThread::Signal。新しいコードでThread::Signalモジュールを使うのはお勧めできません。代わりに、threadsとその関連モジュールを直接利用することを勧めます。threadsの新しい実装ではThread::Signalモジュールと等価なものがありません。明快な方法:シグナルは今や信頼できるかたちでthreadsを使わないPerlプログラミングに配信されます。新しいスレッド機能を使ったシグナル処理は、下支えしているスレッドの実装に追いついています。このスレッド実装はいままさに使われてるところなので、より信頼できないかもしれません。スレッドに特化したシグナルを設定したいなら、他スレッドからのシグナルを処理したいスレッド内で、%SIGハッシュを変更します。これは少なくともLinuxでは動作しているようです。保証はありませんし、これを使った場合の有益性もまちまちです。Mattijsen氏のThread::Signal。このモジュールは、それぞれのスレッド毎に(擬似)プロセスが使われているシステムでしか動きません。私の知るところでは、これが起きるのは(古い)Linuxシステムだけです。この実装が動作する他のOSについても知りたいので、ドキュメントにこれを追加します。Perl5.8.0ではシグナル処理に関するバグのため、%SIGハッシュのエントリは、そのスレッドから開始されるスレッド内で利用される前に代入されなければなりません。シグナルがどんな場合にも実行されるかどうかはまだ明らかではありません。%SIGをセットするまさにそのことが、他のタイムアウト(gethostbyaddr()を使った場合のような)や、他のシグナル(threads::shared::cond_wait()など)が実行されるまでシグナルが受け入れられない原因になります。よって、このモジュールは(%SIGをセットする代わりに)POSIX::sigaction()を利用するように切り替えています。これは少なくとも、より信頼できるようにみえますし、%SIGをセットすることでそのシグナルが活性化しても普通にブロックされるシグナルを送るようにみえます。将来のPerlのバージョンでは%SIGを使ったシグナルの設定が修正されるでしょう。-V:cppflagsとやって-DTHREADS_HAVE_PIDSが出なければ動かないとのこと。仮に出ても動作は保証しないと。Bergman魔術師も踏むを恐れるところ[適当な要約]スレッドとモジュールの関係は?Perlのスレッドシステムは「明示的な共有データ」という考えに基づく。→デフォルトでスレッド間でデータを非共有。これによりスレッドセーフなモジュールの開発を簡単にしてくれる。Pure-Perl(Perlだけで書かれている)なモジュールは本質的にスレッドセーフ。スレッドセーフの3つのレベルスレッドセーフ:マルチスレッドで使っても安全。スレッドフレンドリー:スレッドと一緒に動作して特別な機能を提供するスレッドセーフなモジュール。threads::queueなどが典型。スレッドアンセーフ:別スレッドから利用すると安全でないモジュール。スレッドセーフでないライブラリを利用するXSモジュールなどが典型。ほとんどのPure-Perlはスレッドセーフになるだろうが、それでもソースをレビューするか、作者がスレッドセーフであると明言しない限りは信用するべきではない。全てのXSモジュールがスレッドアンセーフであるというわけでもない。Test::Simpleを例にしたスレッドセーフなモジュールの作りかた。lizPerlのithreadsでプログラミングをする前に知っておくべきこと[適当な要約]5.8.0には共有配列に対してshift()を使ったさいに致命的なまでにメモリを食うバグが存在している。これは5.8.1でfixされている。Perlのithreadsは軽くない。全てのデータ構造はコピーされる。(COW))に生じるのではなく、スレッドの開始時に発生する。共有変数は?→実際には共有していない。実は普通のtieされた変数。→余分にメモリを使うモジュールを使うと全てのスレッドにモジュールがコピーされてしまう。)->join;作者は上記のやり方が好きではないので、Thread::Use(日本語訳)モジュールを作成した。スレッド間のデータ構造をシリアライズ。他のモジュールが利用。様々なコード間におけるスレッドサイズのレポートCPANには他にも色々なThread系モジュールがあります。win32では、Thread::Tieがうまく動作しないようです(後述)。そのため、それを利用するモジュールも正常に動かないっぽいのですが。0.07ooThread::TieをWindowsのPerl5.8.1以降でどうにか動かす方法を見つけました。気が向いたら書きます。forkを使ってthreadsプラグマをエミュレートするので5.8以前のPerlでさえも(その場合Filterが必要ですが)threadsのAPIが利用できます。スレッド間の会話はSocket(TCP/IP)経由ですのでスピードは遅いですが、mod_perl/Apacheとも相性がよいらしいです。cond_timedwaitthreads::sharedにcond_timedwait()が追加されました。タイムアウトの時間はエポック秒で絶対指定。何秒後とかの相対指定はできません。cond_waitに追加の引数をとる形式が追加されました(cond_timedwaitにも同様のものあり)。スレッドの生成に時間がかかる時があるのですが...Perl5.8.4まで、スレッドの生成にかかる時間に大きな幅がありました。1秒間に10個以上生成できることもあれば、1個しか生成できないときもあったりとか。これはスレッド複製ルーチンのハッシュアルゴリズムに問題があったためで、5.8.5で修正されました。その他にも5.8.5では、threads->create()に失敗したときにいきなりPerlが終了するのではなく、undefを返すように動作が変更になりました。(以上perl585deltaより)Thread::Queueについて標準モジュールThread::Queueはスカラーしか扱えない。Thread::Queue::Anyは任意のデータ構造(のリファレンス)も扱うことができる。Thread::Queue::AnyはThread::Conveyorと名前が代わり、インターフェースも全く異なるものになりました。CLONEサブルーチンiスレッドはデータツリーをクローンすることで動作するので、異なるスレッド間でデータは共有されない。......あるスレッドがクローンされるときに、全Perlデータがクローンされる。非Perlデータは自動的にはクローンされない。5.7.2以降のPerlは、CLONEという特別なサブルーチンをサポートしている。CLONE内で、必要なあらゆること、例えば非Perlデータのクローン処理のようなことができる。CLONEは、それが定義されている(あるいはCLONEを継承している)パッケージ毎に一度だけ実行される。これは新しいスレッドのコンテキストで呼び出される。よって、あらゆる変更は新しい領域においてなされる。5.8.0)以下のサンプルで試してみます。Perl5.8.1以降は正しくカレントスレッド内でCLONEが実行されます。0)ok#--------------------------------------上の結果から、CLONEサブルーチン実行時のカレントスレッドは親スレッドであることがわかります。threads->newで新しいスレッドが生成されると、その親スレッドのコンテキストでCLONEサブルーチンは実行されているようです(本当に新しいスレッドのコンテキストで何か初期化処理をしたいならば、Thread::Exitを使います)。ループで示されているように、$CloneTest::CLONEの値は親スレッドから引き継がれていることがわかります。Perl5.8.1以降は正しくカレントスレッド内でCLONEが実行されます。マルチスレッドにおける再現性のある乱数列5.8.0では親スレッドでperl組み込みのrand()を使うと、子スレッドで全て同じ乱数列が発生してしまう。}を使うことだと、Thread::Randには書いてあります。5.8.0)では各スレッド毎に同じ乱数列が発生しました。回避方法を試して見ますと、activeperl5.8.0ではメモリアクセス違反でperlが落ちました。i386-linux-thread-multiでは確かに回避されました。activeperl5.8.1以降ではこの問題が修正されていることを確認。i386-linux-thread-multiも5.8.3において修正を確認(他バージョンは未確認)。親スレッドでコアのsrand(一定値)を使っても、マルチスレッドを通じて再現性のある乱数列が発生しません。そこでThread::Randを使います(要Thread::Tie)。"\n";}これで常に同じ乱数列が表示されます。activeperlで使う他の項でも書きましたが、ithreadにおける共有変数の正体はtieされた変数です。threads::sharedすると暗黙の内に共有変数用のスレッドが生成されます。tieされた変数ですので、共有変数に対してはtieできません。Mattijsen氏の実験的モジュールThread::Tieです。彼女の他の多くのモジュールがこれを利用して、メモリの消費を押さえたり、スレッド間でのデータのやりとりを行っています。ところがこれ、どうもwin32のactiveperlでは動かないようです。試してみたところ、原因は第一にThread::Tie::Thread内のサーバ・クライアント処理でデッドロックしてしまうのと、Thread::Serializeの後処理でメモリアクセス違反を起こしてしまうためではないかと思うのです(確証はありません)。デッドロックの方はT::T::Threadのコードを書き換えれば何とかなるのですが、問題はメモリアクセスの方でして、これはたぶんactiveperlが何とかならないとどうにもならないでしょう。それでも何とかT::T::Threadのコードを書き換えることでかなりまともに動かすことができそうです。5.8.4で、問題の原因であったStorableのバージョンが上がったため、メモリアクセス違反にはならなくなりました(まだデッドロックの問題が残っていますが)。dieしたスレッドがthreads->listのリストに入っているのはおかしいのでは?という質問。perlthrtutには「現在実行されていないか、detachされていないスレッド」のリストであると書いてあるところから。threadsのドキュメントでは「joinもdetachもされていないスレッド」とより正確に書かれている。dieしたスレッドは未だにjoinable。スレッドの生き死にを確認するにはThread::Runningを使うのが吉。PAR(pp)とスレッドPARモジュール付属のppを使うと、perlスクリプトをスタンドアローンな実行形式にできます。threadsしてスレッドを生成するようなコードをスタンドアローンにして実行すると、perlがクラッシュします。回避方法はどん底日誌のこの辺参照。mod_perlmod_perlとithreadsについて。あと、このMLでのやりとりとか。existed.stashとは何かについて。サブルーチンtestの@_に外部から参照されていないリファレンスが残っていると、子スレッドにこれがコピーされて、threadsオブジェクトが破壊される際(サブルーチンの終わり部分)のガベージコレクトで悪さをしているのではないかと思います。\$refとして外部から参照させることでスレッドオブジェクトが破棄されてもガベージコレクトさせないか、あるいは単にサブルーチンの最初でshiftやpopを使って@_から怪しげなものを取り除いてしまうと良いようです。