- 名前
- 説明
- 状況
- スレッドとは何か?
- スレッドプログラムのモデル
- Perlスレッドはどんなスレッドか?
- スレッドセーフなモジュール
- スレッドの基本
- スレッドとデータ
- 同期と制御
- General Thread Utility Routines
- 一般的なスレッドユーティリティルーチン
- 完全な例
- 様々なスレッドの実装
- パフォーマンスの考慮
- プロセススコープの変更
- システムライブラリにおけるスレッドの安全性
- おわりに
- 参考文献
- 謝辞
- 作者
- 著作権
名前¶
perlthrtut - Perlにおけるスレッドのチュートリアル
説明¶
NOTE: this tutorial describes the new Perl threading flavour introduced in Perl 5.6.0 called interpreter threads, or ithreads for short. In this model each thread runs in its own Perl interpreter, and any data sharing between threads must be explicit.
注意:このチュートリアルは、インタプリタスレッド(interpreter threads)、 あるいは短くiスレッド(ithreads)と呼ばれる、Perl 5.6.0において 導入された新しいPerlスレッドの特徴を説明するものである。このモデルにおいては、 それぞれのスレッドはそれ自身のPerlインタプリタで実行され、スレッド間で 共有するデータも明示的でなければならない。
There is another older Perl threading flavour called the 5.005 model, unsurprisingly for 5.005 versions of Perl. The old model is known to have problems, deprecated, and will probably be removed around release 5.10. You are strongly encouraged to migrate any existing 5.005 threads code to the new model as soon as possible.
驚くことではないが、Perlのバージョン5.005には、5.005モデルと呼ばれる 別の古いスレッド機能がある。この古いモデルは問題があることが知られていて、 非推奨である。おそらくバージョン5.10がリリースされるころには無くなるで あろう。できるだけ速やかに既存の5.005スレッドを新しいものに移行することが 強く勧められる。
You can see which (or neither) threading flavour you have by running perl -V
and looking at the Platform
section. If you have useithreads=define
you have ithreads, if you have use5005threads=define
you have 5.005 threads. If you have neither, you don't have any thread support built in. If you have both, you are in trouble.
perl -V
でPlatform
セクションを見れば、どちらのスレッド機能があるのか (あるいは無いのか)を知ることができる。もしuseithreads=define
となって いればiスレッドをサポートしているし、use5005threads=define
とあれば 5.005スレッドだ。両方とも出ない場合、あなたのPerlはスレッドをサポートして いない。両方ある場合は問題がある。
The user-level interface to the 5.005 threads was via the Threads class, while ithreads uses the threads class. Note the change in case.
5.005スレッドへのユーザレベルでのインターフェースは、Thread classを通じて なされる。一方iスレッドはthreads classを使う。大小文字の変化に注意すること。
状況¶
The ithreads code has been available since Perl 5.6.0, and is considered stable. The user-level interface to ithreads (the threads classes) appeared in the 5.8.0 release, and as of this time is considered stable although it should be treated with caution as with all new features.
iスレッドのコードは Perl 5.6.0 から利用できるようになっていて、安定している。 ユーザレベルでのiスレッドへのインターフェース(threads class群)は5.8.0で 登場した。全ての新しい機能同様、注意が必要ではあるとはいえ、これも安定している。
スレッドとは何か?¶
A thread is a flow of control through a program with a single execution point.
スレッドとはプログラム内を通じて一つの実行点を持った制御のフローである。
Sounds an awful lot like a process, doesn't it? Well, it should. Threads are one of the pieces of a process. Every process has at least one thread and, up until now, every process running Perl had only one thread. With 5.8, though, you can create extra threads. We're going to show you how, when, and why.
こういうと非常に多くの点でプロセスに似ているように聞こえるだろう。確かに その通りで、スレッドはひとつのプロセスにおける細かい部分の一つだ。全ての プロセスは少なくとも一つのスレッドを持つ。そしてこれまでは、Perlを走らす プロセスは全てただ一つのスレッドを持っていた。しかし5.8になって、 余分にスレッドをつくることができるようになった。それがいつどのように、 いかにしてなされるのかをこれから示していこう。
スレッドプログラムのモデル¶
There are three basic ways that you can structure a threaded program. Which model you choose depends on what you need your program to do. For many non-trivial threaded programs you'll need to choose different models for different pieces of your program.
スレッドプログラムを構築する3つの基本的な方法がある。どのモデルを選ぶかは プログラムに何をやらせるかに依存する。多くの重要なスレッドプログラムに おいては、プログラム中の異なる部分に異なるモデルを選択する必要があるだろう。
ボス/ワーカー¶
The boss/worker model usually has one `boss' thread and one or more `worker' threads. The boss thread gathers or generates tasks that need to be done, then parcels those tasks out to the appropriate worker thread.
ボス・ワーカーモデルでは通常、一つの「ボス」スレッドと、一つ以上の 「ワーカー」スレッドを持つ。ボススレッドは必要とされるタスクを集約ないし 生成する。それからそれらのタスクを適したワーカースレッドに分配する。
This model is common in GUI and server programs, where a main thread waits for some event and then passes that event to the appropriate worker threads for processing. Once the event has been passed on, the boss thread goes back to waiting for another event.
このモデルはGUIとサーバプログラムに共通である。そこではメインスレッドが イベント発生を待ち、処理のために適したワーカースレッドにイベントを渡す。 ひとたびイベントが渡されると、ボススレッドは次のイベントのために待機する。
The boss thread does relatively little work. While tasks aren't necessarily performed faster than with any other method, it tends to have the best user-response times.
ボススレッドは相対的に仕事量は少ない。タスクは他の方法を使ったものより 必ずしも早く実行されるわけではないが、最もユーザーレスポンスタイムが 良くなる傾向にある。
仕事仲間¶
In the work crew model, several threads are created that do essentially the same thing to different pieces of data. It closely mirrors classical parallel processing and vector processors, where a large array of processors do the exact same thing to many pieces of data.
仕事仲間モデルでは、データの様々な部分に対し本質的に同じ作業を行う いくつかのスレッドがつくられる。これは、プロセッサの巨大な配列が多くの データ片に対して全く同じことを行う古典的な並列処理やベクトル処理と そっくりである。
This model is particularly useful if the system running the program will distribute multiple threads across different processors. It can also be useful in ray tracing or rendering engines, where the individual threads can pass on interim results to give the user visual feedback.
プログラムを走らせているシステムが、異なるプロセスをまたいでマルチスレッドを 分配するような場合、このモデルは殊のほか便利である。また、レイトレースや レンダリングエンジンといった、個々のスレッドが暫定的な結果をユーザに対し 視覚的にフィードバックするような場合にも便利であろう。
パイプライン¶
The pipeline model divides up a task into a series of steps, and passes the results of one step on to the thread processing the next. Each thread does one thing to each piece of data and passes the results to the next thread in line.
パイプラインモデルはあるタスクを何段階の処理の連続へと分ける。そして 一つのステップの結果を次の処理を行うスレッドへと渡す。それぞれのスレッドは データの各部分に対し一つのことを行い、結果を次のスレッドへ渡す。
This model makes the most sense if you have multiple processors so two or more threads will be executing in parallel, though it can often make sense in other contexts as well. It tends to keep the individual tasks small and simple, as well as allowing some parts of the pipeline to block (on I/O or system calls, for example) while other parts keep going. If you're running different parts of the pipeline on different processors you may also take advantage of the caches on each processor.
しばしば他の文脈でも同じぐらいの意味があるのだが、二つ以上のスレッドが 並列に処理を行うようにあなたがマルチプロセッサを持っているならば、この モデルは最も意味がある。それは、あるパイプラインの一部が進行中の間に、 他のパイプラインの一部が(例えばI/Oやシステムコールを)ブロックするのを 許可するのと同様、個々のタスクを小さく単純なものに留める傾向がある。 もし違うプロセッサ上で別々のパイプラインを走らせるならば、それぞれの プロセッサのキャッシュを利用するという利益を得ることができるだろう。
This model is also handy for a form of recursive programming where, rather than having a subroutine call itself, it instead creates another thread. Prime and Fibonacci generators both map well to this form of the pipeline model. (A version of a prime number generator is presented later on.)
このモデルはまた、自分自身を呼び出すサブルーチンを持つよりも、別の スレッドを生み出すような再起的プログラムの形態に利用しやすい。素数や フィボナッチ数列ジェネレーターは、どちらもこのパイプラインモデルの形態に うまく位置付けられる(素数ジェネレーターの例は後で登場する)。
Perlスレッドはどんなスレッドか?¶
If you have experience with other thread implementations, you might find that things aren't quite what you expect. It's very important to remember when dealing with Perl threads that Perl Threads Are Not X Threads, for all values of X. They aren't POSIX threads, or DecThreads, or Java's Green threads, or Win32 threads. There are similarities, and the broad concepts are the same, but if you start looking for implementation details you're going to be either disappointed or confused. Possibly both.
もしもあなたが他のスレッドの実装を経験したことがあるなら、事態は全く あなたの予想とは違うことがわかるだろう。Perlスレッドを扱う時には、 "PerlスレッドはXスレッドではない"(Xはあらゆる値)ということを 忘れてはならない。それらはPOSIXスレッドではない。DecThreadでもなければ Javaのグリーンスレッドでないし、Win32スレッドでもない。類似点はあるし、 広義の概念は同じだ。だが実装の詳細を調べだしたら、あなたは失望するか 混乱するかのどちらかになるだろう。あるいはその両方かもしれない。
This is not to say that Perl threads are completely different from everything that's ever come before--they're not. Perl's threading model owes a lot to other thread models, especially POSIX. Just as Perl is not C, though, Perl threads are not POSIX threads. So if you find yourself looking for mutexes, or thread priorities, it's time to step back a bit and think about what you want to do and how Perl can do it.
これは、これまで登場してきたあらゆるスレッドとPerlスレッドが完全に 異なるということを言っているのではない。Perlスレッドは他のモデル、 特にPOSIXスレッドに多くを負っている。だが、PerlがCではないように、 PerlスレッドはPOSIXスレッドではない。だから、もしあなたがミューテックスや スレッドプライオリティを期待しているならば、ちょっと立ち戻って、自分が何を したいのか、Perlはいかにしてそれが可能なのかを考える時だ。
However it is important to remember that Perl threads cannot magically do things unless your operating systems threads allows it. So if your system blocks the entire process on sleep(), Perl usually will as well.
しかし、あなたのオペレーティングシステムのスレッドが許可しない限り、 Perlスレッドが魔法のように何かを行うことはできないことを覚えておこう。 だからもし、システムがsleep()でプロセスを丸ごとブロックするなら、通常 Perlも同様にそうするだろう。
Perl Threads Are Different.
Perlスレッドは別ものなのだ。
スレッドセーフなモジュール¶
The addition of threads has changed Perl's internals substantially. There are implications for people who write modules with XS code or external libraries. However, since perl data is not shared among threads by default, Perl modules stand a high chance of being thread-safe or can be made thread-safe easily. Modules that are not tagged as thread-safe should be tested or code reviewed before being used in production code.
スレッドの追加はPerlの内部的な実態を変化させてしまった。これには XSモジュールや外部ライブラリーを書いている人々も含まれる。しかしながら、 デフォルトではスレッド間でperlのデータは共有されない。そのためPerlモジュールは スレッドセーフであるという機会に恵まれているし、あるいは容易にスレッドセーフに することができる。スレッドセーフの札がついていないモジュールは製品版の前に テストされるかコードレビューを受けるべきである。
Not all modules that you might use are thread-safe, and you should always assume a module is unsafe unless the documentation says otherwise. This includes modules that are distributed as part of the core. Threads are a new feature, and even some of the standard modules aren't thread-safe.
あなたが使おうとしているモジュールの全てがスレッドセーフというわけではないし、 モジュールのドキュメントに安全であると書かれていないならば、常に安全ではない ものと仮定するべきである。このことは、perlのコア部分として配布されている モジュールにもあてはまる。スレッドは新しい機構なのであり、標準モジュールの 中にさえスレッドセーフでないものがある。
Even if a module is thread-safe, it doesn't mean that the module is optimized to work well with threads. A module could possibly be rewritten to utilize the new features in threaded Perl to increase performance in a threaded environment.
仮にあるモジュールがスレッドセーフであったとしても、そのモジュールが うまく働くように最適化されているということを意味するわけではない。できるだけ スレッド環境でパフォーマンスが向上するように、スレッド化されたPerlの新機構を 利用するようモジュールを書き直したほうがよい。
If you're using a module that's not thread-safe for some reason, you can protect yourself by using it from one, and only one thread at all. If you need multiple threads to access such a module, you can use semaphores and lots of programming discipline to control access to it. Semaphores are covered in "Basic semaphores".
もしもなんらかの理由でスレッドセーフではないモジュールを使っているならば、 ただ一つのスレッドからそれを使うことによって防ぐことができる。そのような モジュールにアクセスするマルチスレッドが必要ならば、セマフォやアクセスを 制御するためのプログラミング原則を用いることができる。セマフォは "ベーシックなセマフォ"でカバーしている。
See also "Thread-Safety of System Libraries".
また、"システムライブラリにおけるスレッドの安全性"を参照のこと。
スレッドの基本¶
The core threads module provides the basic functions you need to write threaded programs. In the following sections we'll cover the basics, showing you what you need to do to create a threaded program. After that, we'll go over some of the features of the threads module that make threaded programming easier.
中核となるthreadsモジュールは、あなたがスレッドプログラムを書くのに必要と なる基本的な機能を提供する。次からのセクションでは、スレッドプログラムを作る のに必要なことを示しながら基礎的なところをカバーしていこう。その後で、スレッド プログラミングを容易にしてくれるthreadsモジュールの機能をみることにする。
基本的なスレッドのサポート¶
Thread support is a Perl compile-time option - it's something that's turned on or off when Perl is built at your site, rather than when your programs are compiled. If your Perl wasn't compiled with thread support enabled, then any attempt to use threads will fail.
スレッドのサポートは、あなたのプログラムがコンパイルされる時ではなく、 Perlのコンパイル時のオプションによる。つまり、Perlをビルトするときに、 サポートのオン・オフを行う。スレッドがサポートされるようにPerlがコンパイル されていないなら、スレッドを使おうとしても失敗するだろう。
Your programs can use the Config module to check whether threads are enabled. If your program can't run without them, you can say something like:
スレッドが使用可能かどうかをチェックするためにConfigモジュールを利用する ことができる。あなたのプログラムがスレッド無しには実行できないなら、 次のように記述することができる:
$Config{useithreads} or die "Recompile Perl with threads to run this program.";
A possibly-threaded program using a possibly-threaded module might have code like this:
スレッドを利用するかもしれないモジュールを使うプログラムには、 次のようなコードをつけておくとよい:
use Config;
use MyMod;
BEGIN {
if ($Config{useithreads}) {
# スレッドが利用できる
require MyMod_threaded;
import MyMod_threaded;
} else {
require MyMod_unthreaded;
import MyMod_unthreaded;
}
}
Since code that runs both with and without threads is usually pretty messy, it's best to isolate the thread-specific code in its own module. In our example above, that's what MyMod_threaded is, and it's only imported if we're running on a threaded Perl.
スレッドの有無に関わらず走るようなコードは通常、非常に繁雑なものになるので、 スレッド専用のコードはモジュールとして分離しておくのがベストだ。上の例では、 MyMod_threadedがそれで、スレッド可能なPerl上で走るときだけインポートされる。
例に対する注意¶
Although thread support is considered to be stable, there are still a number of quirks that may startle you when you try out any of the examples below. In a real situation, care should be taken that all threads are finished executing before the program exits. That care has not been taken in these examples in the interest of simplicity. Running these examples "as is" will produce error messages, usually caused by the fact that there are still threads running when the program exits. You should not be alarmed by this. Future versions of Perl may fix this problem.
スレッドのサポートが安定しているとはいえ、下の例を試すときには、あなたを 驚かせるような奇妙なことがまだたくさん起きる。実際の状況では、プログラムが 終了する前に全てのスレッドが実行を終えることに注意を払わなければならない。 簡明さを重視しているのでここで取り上げる例においては、そのような注意を 払っていない。これらの例を"そのまま"実行すると、プログラムが終了する際に まだスレッドが走っているという理由で常にエラーメッセージがはき出されるだろう。 この警告は気にしなくて良い。今後のPerlのバージョンではこの問題は 取り除かれるであろう。
スレッドの生成¶
The threads package provides the tools you need to create new threads. Like any other module, you need to tell Perl that you want to use it; use threads
imports all the pieces you need to create basic threads.
threadsパッケージは新たなスレッドを生成するのに必要なツールを提供する。 他のモジュール同様、それを使いたいとPerlに伝えればよい; use threads
に よって基本的なスレッドを生み出すのに必要な全ての部品がインポートされる。
The simplest, most straightforward way to create a thread is with new():
new() による最も単純で直接的なスレッドの生成:
use threads;
$thr = threads->new(\&sub1);
sub sub1 {
print "In the thread\n";
}
The new() method takes a reference to a subroutine and creates a new thread, which starts executing in the referenced subroutine. Control then passes both to the subroutine and the caller.
new()
メソッドはサブルーチンへのリファレンスを引数にとって新しいスレッドを 生成する。このスレッドはリファレンスされたサブルーチンの実行を開始する。 このとき制御はサブルーチンと呼び出し側との両方に渡される。
If you need to, your program can pass parameters to the subroutine as part of the thread startup. Just include the list of parameters as part of the threads::new
call, like this:
もし必要ならばスレッド開始時のサブルーチンにパラメータを渡すことができる。 以下のように、threads::new
の呼び出しにパラメータのリストを含める:
use threads;
$Param3 = "foo";
$thr = threads->new(\&sub1, "Param 1", "Param 2", $Param3);
$thr = threads->new(\&sub1, @ParamList);
$thr = threads->new(\&sub1, qw(Param1 Param2 Param3));
sub sub1 {
my @InboundParameters = @_;
print "In the thread\n";
print "got parameters >", join("<>", @InboundParameters), "<\n";
}
The last example illustrates another feature of threads. You can spawn off several threads using the same subroutine. Each thread executes the same subroutine, but in a separate thread with a separate environment and potentially separate arguments.
この例はスレッドのもう一つの特徴を示している。同じサブルーチンを利用する いくつものスレッドを生成することができるのだ。それぞれのスレッドは同一の サブルーチンを実行するが、それぞれのスレッドはそれぞれ別々の環境と引数を とることができる。
create()
is a synonym for new()
.
create()
はnew()
の言い換えである。
スレッド終了の待機¶
Since threads are also subroutines, they can return values. To wait for a thread to exit and extract any values it might return, you can use the join() method:
スレッドはサブルーチンでもあるから、値を返すことができる。スレッドが 終了して何らかの戻り値を得るのを待つために、join()を使うことができる:
use threads;
$thr = threads->new(\&sub1);
@ReturnData = $thr->join;
print "Thread returned @ReturnData";
sub sub1 { return "Fifty-six", "foo", 2; }
In the example above, the join() method returns as soon as the thread ends. In addition to waiting for a thread to finish and gathering up any values that the thread might have returned, join() also performs any OS cleanup necessary for the thread. That cleanup might be important, especially for long-running programs that spawn lots of threads. If you don't want the return values and don't want to wait for the thread to finish, you should call the detach() method instead, as described next.
上の例では、スレッドが終了するやいなjoin()メソッドが戻る。スレッドの終了と、 返すべき値を収集するための待機に加えて、join()はスレッドが必要とする OSレベルのクリーンナップを実行する。このクリーンナップは重要だ。特に 長時間にわたって実行されるプログラムが大量のスレッドを生成する場合には。 もしも戻り値を必要とせず、スレッドの終了を待つ必要もなければ、かわりに 次に説明するdetach()メソッドを呼び出すべきだ。
スレッドを無視する¶
join() does three things: it waits for a thread to exit, cleans up after it, and returns any data the thread may have produced. But what if you're not interested in the thread's return values, and you don't really care when the thread finishes? All you want is for the thread to get cleaned up after when it's done.
join()は三つのことを行う。スレッド終了の待機、その後のクリーンナップ、 そしてスレッドが生み出したであろうデータを返すことだ。しかし、スレッドの 返す値に関心がなく、いつスレッドが終了するのかを本当に気にしない場合には? 必要なのは仕事がなされた後にスレッドがクリーンナップされることだけだ。
In this case, you use the detach() method. Once a thread is detached, it'll run until it's finished, then Perl will clean up after it automatically.
このような場合、detach()メソッドを使う。ひとたびスレッドがdetachされると、 スレッドは終了するまで実行し続け、そのあとPerlが自動的にクリーンナップをする。
use threads;
$thr = threads->new(\&sub1); # スレッドを生む
$thr->detach; # いまや正式に注意を払わない
sub sub1 {
$a = 0;
while (1) {
$a++;
print "\$a is $a\n";
sleep 1;
}
}
Once a thread is detached, it may not be joined, and any return data that it might have produced (if it was done and waiting for a join) is lost.
ひとたびあるスレッドがdetachされたら、そのスレッドはjoinされないだろう。 (joinのために待機しても)スレッドが生成したであろうデータは失われる。
スレッドとデータ¶
Now that we've covered the basics of threads, it's time for our next topic: data. Threading introduces a couple of complications to data access that non-threaded programs never need to worry about.
いまやスレッドの基本部分については見終わった。次の話題はデータだ。 スレッドを扱うと、非スレッドプログラムが決して心配することのなかった データアクセスに対する二つの複雑さを導入することになる。
共有データと非共有データ¶
The biggest difference between Perl ithreads and the old 5.005 style threading, or for that matter, to most other threading systems out there, is that by default, no data is shared. When a new perl thread is created, all the data associated with the current thread is copied to the new thread, and is subsequently private to that new thread! This is similar in feel to what happens when a UNIX process forks, except that in this case, the data is just copied to a different part of memory within the same process rather than a real fork taking place.
iスレッドと古い5.005型スレッドの間の(もっといえば、そこから外れる多くの スレッドシステムにとっての)最大の違いは、デフォルトではデータが共有されない という点だ。新しいperlスレッドが生成されるとき、現在のスレッドに関連する 全てのデータは新しいスレッドにコピーされる。続いてそのデータは新しい スレッド内でプライベートなものとなる! これはUNIXのプロセスがforkするときに 起きることと似ている。ただしこの場合、実際のforkではメモリ上での置き換えが 起こるのに対して、このデータは同一プロセッサ内の違うメモリ部分にコピーされる だけであるという点が除かれる。
To make use of threading however, one usually wants the threads to share at least some data between themselves. This is done with the threads::shared module and the : shared
attribute:
しかしスレッド機能を利用するならば、通常はスレッド間で少なくともいくつかの データを共有したいものだ。これは threads::sharedモジュールと : shared
属性によってなされる:
use threads;
use threads::shared;
my $foo : shared = 1;
my $bar = 1;
threads->new(sub { $foo++; $bar++ })->join;
print "$foo\n"; # $fooは共有されているので2を出力
print "$bar\n"; # $barは共有されていないので1を出力
In the case of a shared array, all the array's elements are shared, and for a shared hash, all the keys and values are shared. This places restrictions on what may be assigned to shared array and hash elements: only simple values or references to shared variables are allowed - this is so that a private variable can't accidentally become shared. A bad assignment will cause the thread to die. For example:
共有化された配列の場合は、配列の要素全てが共有化される。共有化されたハッシュの 場合、全ての key と value が共有される。共有化された配列やハッシュの要素に 代入するものに対しては制限がある:単純な値や共有化された変数へのリファレンスは 可能だ。これは、プライベート変数が図らずも共有化されることがないからだ。 不正な代入はスレッドを殺してしまうだろう。例えば:
use threads;
use threads::shared;
my $var = 1;
my $svar : shared = 2;
my %hash : shared;
... create some threads ...
$hash{a} = 1; # どのスレッドからも exists($hash{a}) and $hash{a} == 1
$hash{a} = $var # OK、値のコピーだ。効果は上に同じ。
$hash{a} = $svar # OK、値のコピーだ。効果は上に同じ。
$hash{a} = \$svar # OK、共有変数へのリファレンスだ。
$hash{a} = \$var # これはダメだろう。
delete $hash{a} # OK、どのスレッドからも !exists($hash{a})
Note that a shared variable guarantees that if two or more threads try to modify it at the same time, the internal state of the variable will not become corrupted. However, there are no guarantees beyond this, as explained in the next section.
共有変数が、複数のスレッドが同時にその変数に変更を加えようとしても、 変数の内部状態は破壊されないことを保証していることに注意。しかし次の セクションで説明するように、ここを超えてしまうと保証はなくなってしまう。
スレッドの落とし穴:競合¶
While threads bring a new set of useful tools, they also bring a number of pitfalls. One pitfall is the race condition:
スレッドは新しい便利なツールを一揃いもたらしてくれる一方で、たくさんの 落とし穴ももたらす。その一つは競合条件である:
use threads;
use threads::shared;
my $a : shared = 1;
$thr1 = threads->new(\&sub1);
$thr2 = threads->new(\&sub2);
$thr1->join;
$thr2->join;
print "$a\n";
sub sub1 { my $foo = $a; $a = $foo + 1; }
sub sub2 { my $bar = $a; $a = $bar + 1; }
What do you think $a will be? The answer, unfortunately, is "it depends." Both sub1() and sub2() access the global variable $a, once to read and once to write. Depending on factors ranging from your thread implementation's scheduling algorithm to the phase of the moon, $a can be 2 or 3.
$aはどうなるだろうか?残念ながら、その答えは"場合によりけり"だ。 sub1()とsub2()はどちらも一度だけ読み込み、一度だけ書き込むためにグローバル 変数$aにアクセスする。あなたの使っているスレッド実装のスケジューリング アルゴリズムから月の満ち欠けまでの種々の要因によって、$aは2にも3にもなりうる。
Race conditions are caused by unsynchronized access to shared data. Without explicit synchronization, there's no way to be sure that nothing has happened to the shared data between the time you access it and the time you update it. Even this simple code fragment has the possibility of error:
競合条件は共有データに対して非同期なアクセスを行うことによって生じる。 明示的に同期をとらない限り、アクセスして更新するまでの間に 共有データに何も起こらないことを確実にする方法はない。こんな単純な コードでさえ、エラーの可能性があるのだ:
use threads;
my $a : shared = 2;
my $b : shared;
my $c : shared;
my $thr1 = threads->create(sub { $b = $a; $a = $b + 1; });
my $thr2 = threads->create(sub { $c = $a; $a = $c + 1; });
$thr1->join;
$thr2->join;
Two threads both access $a. Each thread can potentially be interrupted at any point, or be executed in any order. At the end, $a could be 3 or 4, and both $b and $c could be 2 or 3.
二つのスレッドが$aにアクセスする。それぞれのスレッドはいずれかの時点で 割り込まれたり、いずれかの順番で実行される可能性がある。結局、$aは3か4に、 $bと$cはともに2か3になるだろう。
Even $a += 5
or $a++
are not guaranteed to be atomic.
$a += 5
や$a++
でさえ、アトミックな演算であることを保証できない。
Whenever your program accesses data or resources that can be accessed by other threads, you must take steps to coordinate access or risk data inconsistency and race conditions. Note that Perl will protect its internals from your race conditions, but it won't protect you from you.
他のスレッドによってアクセスされる可能性のあるデータやリソースにあなたの プログラムがアクセスするときはいつでも、整合的なアクセスのための手順を 踏まなければならない。さもなければデータの一貫性が崩れたり、競合条件に 陥るリスクを負うことになる。
同期と制御¶
Perl provides a number of mechanisms to coordinate the interactions between themselves and their data, to avoid race conditions and the like. Some of these are designed to resemble the common techniques used in thread libraries such as pthreads
; others are Perl-specific. Often, the standard techniques are clumsy and difficult to get right (such as condition waits). Where possible, it is usually easier to use Perlish techniques such as queues, which remove some of the hard work involved.
競合条件やそれに似た状態を回避するために、スレッドとデータとの間の相互作用を 調整する多くのメカニズムをPerlは提供する。それらのうちのあるものは、 pthreads
のようなスレッドライブラリにおいて使われる共通のテクニックに似た 設計がなされている。またあるものはPerlに特化している。しばしば標準的な テクニックというものはぎこちないものであり、正しく扱うには難しい。可能な ところでは通常、キューのようなPerl的テクニックはより簡単であり、難しい仕事の うちのあるものを取り除いてくれる。
アクセス制御:lock()¶
The lock() function takes a shared variable and puts a lock on it. No other thread may lock the variable until the variable is unlocked by the thread holding the lock. Unlocking happens automatically when the locking thread exits the outermost block that contains lock()
function. Using lock() is straightforward: this example has several threads doing some calculations in parallel, and occasionally updating a running total:
lock()関数は共有変数を引数にとり、それにロックをかける。ロックを保持する スレッドによって変数のロックが解除されるまで、他のスレッドはロックを かけることができない。ロックしているスレッドが lock()
関数を含むブロックの 外に出るとロックは自動的に解除される。lock()の利用は率直なやり方だ。 この例は、ある計算を並列的に処理し、時折その総計値を更新するいくつかの スレッドを伴っている:
use threads;
use threads::shared;
my $total : shared = 0;
sub calc {
for (;;) {
my $result;
# (... 何か計算を行って、その値を $result に入れる ...)
{
lock($total); # ロックが成功するまでブロック
$total += $result;
} # スコープの最後で暗黙的にロックは開放される
last if $result == 0;
}
}
my $thr1 = threads->new(\&calc);
my $thr2 = threads->new(\&calc);
my $thr3 = threads->new(\&calc);
$thr1->join;
$thr2->join;
$thr3->join;
print "total=$total\n";
lock() blocks the thread until the variable being locked is available. When lock() returns, your thread can be sure that no other thread can lock that variable until the outermost block containing the lock exits.
ロックされている変数が利用可能になるまで、lock()はそのスレッドをブロックする。 lock()が戻ってくると、そのロックが存在しているブロックの一番外に出ない限り、 他のスレッドがその変数をロックできないことが確実になる。
It's important to note that locks don't prevent access to the variable in question, only lock attempts. This is in keeping with Perl's longstanding tradition of courteous programming, and the advisory file locking that flock() gives you.
ロックは問題の変数にアクセスするのを阻止するのではなくて、ロックの試みを 阻止するのだということに注意することが重要だ。これはPerlの長年にわたる 作法どおりのプログラミングの伝統と、flock()が提供する勧告ロックとに調和する ものである。
You may lock arrays and hashes as well as scalars. Locking an array, though, will not block subsequent locks on array elements, just lock attempts on the array itself.
スカラー変数同様、配列やハッシュにもロックをかけるかもしれない。 しかし、配列へのロックは、配列の要素に対する二次的なロックをブロックする のではなく、配列そのものへのロックの試みをブロックする。
Locks are recursive, which means it's okay for a thread to lock a variable more than once. The lock will last until the outermost lock() on the variable goes out of scope. For example:
ロックは再帰的、つまりスレッドはある変数に対し何度もロックをかけて大丈夫だ。 一番外側のlock()がスコープを抜けるまでロックは続く。例えば:
my $x : shared;
doit();
sub doit {
{
{
lock($x); # ロックするまで待つ
lock($x); # 無駄 - 既にロックしている
{
lock($x); # 無駄
{
lock($x); # 無駄
lockit_some_more();
}
}
} # *** ここで暗黙的なロック解除 ***
}
}
sub lockit_some_more {
lock($x); # 無駄
} # ここでは何も起こらない
Note that there is no unlock() function - the only way to unlock a variable is to allow it to go out of scope.
unlock()関数は無いことに注意。変数のロックを解除する方法はスコープを 抜けさせることだけだ。
A lock can either be used to guard the data contained within the variable being locked, or it can be used to guard something else, like a section of code. In this latter case, the variable in question does not hold any useful data, and exists only for the purpose of being locked. In this respect, the variable behaves like the mutexes and basic semaphores of traditional thread libraries.
ロックされている変数内に保持されているデータをガードするか、あるいは コードのセクションのようなものを守るために、ロックは用いられる。後者の場合、 問題の変数は役に立つデータを持たず、ロックの目的のためにだけ存在する。 この点からすると、その変数は伝統的なスレッドライブラリにおけるミューテックスや 基本的なセマフォのような振る舞いをするといえる。
スレッドの落とし穴:デッドロック¶
Locks are a handy tool to synchronize access to data, and using them properly is the key to safe shared data. Unfortunately, locks aren't without their dangers, especially when multiple locks are involved. Consider the following code:
ロックはデータに同期アクセスするための便利なツールである。適切に使えば 安全な共有データへの鍵となる。残念なことに、ロックには危険が伴う。特に 複数のロックが関わるとそうなる。次のコードを考えてみよう:
use threads;
my $a : shared = 4;
my $b : shared = "foo";
my $thr1 = threads->new(sub {
lock($a);
sleep 20;
lock($b);
});
my $thr2 = threads->new(sub {
lock($b);
sleep 20;
lock($a);
});
This program will probably hang until you kill it. The only way it won't hang is if one of the two threads acquires both locks first. A guaranteed-to-hang version is more complicated, but the principle is the same.
このプログラムは恐らくあなたがkillするまで固まってしまうだろう。 ハングさせないための唯一の方法は、どちらかのスレッドが先に両方のロックを 獲得することだ。ハングを保証する方法はもっと複雑だが、原理はこれと同じだ。
The first thread will grab a lock on $a, then, after a pause during which the second thread has probably had time to do some work, try to grab a lock on $b. Meanwhile, the second thread grabs a lock on $b, then later tries to grab a lock on $a. The second lock attempt for both threads will block, each waiting for the other to release its lock.
最初のスレッドが$aのロックを手にする。それから二つ目のスレッドが何か やっている間に一時停止した後、$bへのロックを手に入れようと試みる。ところが、 その二つ目のスレッドは$bへのロックを手にする。そしてその後$aのロックを 手に入れようとする。それぞれのスレッドが、他方のスレッドのロックが 開放されるの待つため、二番目のロックへの試みはブロックしてしまう。
This condition is called a deadlock, and it occurs whenever two or more threads are trying to get locks on resources that the others own. Each thread will block, waiting for the other to release a lock on a resource. That never happens, though, since the thread with the resource is itself waiting for a lock to be released.
この状態をデッドロックという。二つ以上のスレッドが他スレッドの所有する リソースをロックしようと試みるときにはいつでも生じる。他のスレッドが リソースへのロックを開放するのを待って、互いにスレッドがブロックする。しかし、 リソースを持っているスレッドが自分自身のロックの開放を待つ場合は生じない。
There are a number of ways to handle this sort of problem. The best way is to always have all threads acquire locks in the exact same order. If, for example, you lock variables $a, $b, and $c, always lock $a before $b, and $b before $c. It's also best to hold on to locks for as short a period of time to minimize the risks of deadlock.
この種の問題を扱う方法はたくさんある。最もよい方法は、常に全てのスレッドが 正確に同じ順番でロックを獲得するようにさせることだ。例えば、$a、$b、$cを ロックするなら、いつでも$bの前に$aを、そして$cの前に$bをロックするようにする。 デッドロックの危険を最小にするために、短い時間だけロックを保持するのも良い手だ。
The other synchronization primitives described below can suffer from similar problems.
下記で説明するような他のプリミティブな同期も同様な問題を抱えうる。
キュー:データの受け渡し¶
A queue is a special thread-safe object that lets you put data in one end and take it out the other without having to worry about synchronization issues. They're pretty straightforward, and look like this:
キューとは、一方の端にデータを入れ、他方から取り出すことによって、 同期の問題を心配しなくてすむ、特別なスレッドセーフオブジェクトである。 これらは非常に素朴で、こんな感じだ。
use threads;
use Thread::Queue;
my $DataQueue = Thread::Queue->new;
$thr = threads->new(sub {
while ($DataElement = $DataQueue->dequeue) {
print "Popped $DataElement off the queue\n";
}
});
$DataQueue->enqueue(12);
$DataQueue->enqueue("A", "B", "C");
$DataQueue->enqueue(\$thr);
sleep 10;
$DataQueue->enqueue(undef);
$thr->join;
You create the queue with new Thread::Queue
. Then you can add lists of scalars onto the end with enqueue(), and pop scalars off the front of it with dequeue(). A queue has no fixed size, and can grow as needed to hold everything pushed on to it.
new Thread::Queue
でキューを生成する。それからenqueue()を使ってキューの 最後にスカラーのリストを追加する。そしてdequeue()でキューの前方からスカラーを 取り出す。キューのサイズは固定していない。キューの中に押し込められたものを 保持する必要に応じてサイズは成長する。
If a queue is empty, dequeue() blocks until another thread enqueues something. This makes queues ideal for event loops and other communications between threads.
もしキューが空っぽの場合、他のスレッドが何かをキューの中に入れるまで、 dequeue()はブロックする。そのため、イベントループやスレッド間での コミュニケーションにとって、キューは理想的だ。
セマフォ:データアクセスの同期¶
Semaphores are a kind of generic locking mechanism. In their most basic form, they behave very much like lockable scalars, except that thay can't hold data, and that they must be explicitly unlocked. In their advanced form, they act like a kind of counter, and can allow multiple threads to have the 'lock' at any one time.
セマフォは包括的なロックメカニズムの一種である。最も基本となる形態では、 セマフォはデータを持つことはできないし、明示的にロック解除されなければ ならないことを除くと、ロック可能なスカラーそっくりに振舞う。発展的な 形態においては、一種のカウンターのように動作し、いつでも複数のスレッドが "ロック"を持つことを可能にする。
ベーシックなセマフォ¶
Semaphores have two methods, down() and up(): down() decrements the resource count, while up increments it. Calls to down() will block if the semaphore's current count would decrement below zero. This program gives a quick demonstration:
セマフォは二つのメソッド、down()とup()を持つ。down()はリソースのカウントを 減少させ、up()の方は増加させる。セマフォの現在のカウントが0を下回るようなら down()の呼び出しはブロックする。このプログラムは短い例だ:
use threads;
use Thread::Semaphore;
my $semaphore = new Thread::Semaphore;
my $GlobalVariable : shared = 0;
$thr1 = new threads \&sample_sub, 1;
$thr2 = new threads \&sample_sub, 2;
$thr3 = new threads \&sample_sub, 3;
sub sample_sub {
my $SubNumber = shift @_;
my $TryCount = 10;
my $LocalCopy;
sleep 1;
while ($TryCount--) {
$semaphore->down;
$LocalCopy = $GlobalVariable;
print "$TryCount tries left for sub $SubNumber (\$GlobalVariable is $GlobalVariable)\n";
sleep 2;
$LocalCopy++;
$GlobalVariable = $LocalCopy;
$semaphore->up;
}
}
$thr1->join;
$thr2->join;
$thr3->join;
The three invocations of the subroutine all operate in sync. The semaphore, though, makes sure that only one thread is accessing the global variable at once.
このサブルーチンに対する三つの呼び出しは同時に作用する。しかし一度に一つの スレッドだけが、グローバル変数にアクセスすることをセマフォが保証する。
高度なセマフォ¶
By default, semaphores behave like locks, letting only one thread down() them at a time. However, there are other uses for semaphores.
デフォルトでは、セマフォはロックのように振舞う。一度にただ一つのスレッド だけがdown()できる。しかし、セマフォには別の使い方がある。
Each semaphore has a counter attached to it. By default, semaphores are created with the counter set to one, down() decrements the counter by one, and up() increments by one. However, we can override any or all of these defaults simply by passing in different values:
それぞれのセマフォは、関連付けられたカウンタを持つ。デフォルトでセマフォは 生成時、カウンタに1がセットされる。down()はカウンタから1を引き、up()は1を足す。 しかしこの規定値の一部ないしは全部を別の値でもって上書きすることができる:
use threads;
use Thread::Semaphore;
my $semaphore = Thread::Semaphore->new(5);
# カウンタに5をセットしてセマフォを生成
$thr1 = threads->new(\&sub1);
$thr2 = threads->new(\&sub1);
sub sub1 {
$semaphore->down(5); # カウンタから5を引く
# ここで何かして
$semaphore->up(5); # カウンタに5を足す
}
$thr1->detach;
$thr2->detach;
If down() attempts to decrement the counter below zero, it blocks until the counter is large enough. Note that while a semaphore can be created with a starting count of zero, any up() or down() always changes the counter by at least one, and so $semaphore->down(0) is the same as $semaphore->down(1).
もしもdown()がカウンタを0より小さい値に下げようとするなら、down()は カウンタが十分な大きさになるまでブロックする。セマフォはカウンタの値を0にして 生成することができる一方、up()やdown()は常に、少なくとも1の変化をカウンタに 対して行うことに注意すること。だから、$semaphore->down(0)は$semaphore->down(1) と一緒だ。
The question, of course, is why would you do something like this? Why create a semaphore with a starting count that's not one, or why decrement/increment it by more than one? The answer is resource availability. Many resources that you want to manage access for can be safely used by more than one thread at once.
もちろん、問題はどうしてこんなことをするのかということだ。なぜセマフォを 1ではなくて、0から始めるように生成するのか?あるいは、なぜ1以上の値で増減を 行うのか?その答えはリソースの利用可能性だ。あなたがアクセス管理をしたい 多くのリソースは、同時に一つ以上のスレッドによって安全に利用される。
For example, let's take a GUI driven program. It has a semaphore that it uses to synchronize access to the display, so only one thread is ever drawing at once. Handy, but of course you don't want any thread to start drawing until things are properly set up. In this case, you can create a semaphore with a counter set to zero, and up it when things are ready for drawing.
例としてGUI駆動型のプログラムを取り上げてみよう。プログラムは、ディスプレイに 同期アクセスするためにセマフォを持っている。そうして一度にただ一つのスレッド だけが描画を行っている。簡単な話だが、もちろん、ちゃんと準備ができるまで どのスレッドにも描画を始めてもらいたくはない。こんな場合に、カウンタを0にして セマフォを生成するのだ。そして描画の準備ができたら、カウンタを増加させる。
Semaphores with counters greater than one are also useful for establishing quotas. Say, for example, that you have a number of threads that can do I/O at once. You don't want all the threads reading or writing at once though, since that can potentially swamp your I/O channels, or deplete your process' quota of filehandles. You can use a semaphore initialized to the number of concurrent I/O requests (or open files) that you want at any one time, and have your threads quietly block and unblock themselves.
1より大きいカウンタを持つセマフォはクォータの確立にも役立つ。例えば、 同時にI/Oを使うスレッドがいくつもあるとする。だが、全てのスレッドが同時に 読み書きをするのは望まない。そんなことをしたらI/Oチャンネルを圧迫するかも しれないし、プロセスに割り当てられたファイルハンドルを枯渇させてしまうかも しれないからだ。あなたがいつでも必要とし、スレッドにおとなしく ブロック・アンブロックをさせたいだけの現I/Oリクエスト数 (あるいはオープンファイル数)で初期設定されたセマフォを利用すればよい。
Larger increments or decrements are handy in those cases where a thread needs to check out or return a number of resources at once.
あるスレッドが一度にたくさんのリソースを借りたり戻したりするような こういうケースでは、大きな数で増減を行うのが便利だ。
cond_wait()とcond_signal()¶
These two functions can be used in conjunction with locks to notify co-operating threads that a resource has become available. They are very similar in use to the functions found in pthreads
. However for most purposes, queues are simpler to use and more intuitive. See threads::shared for more details.
これら二つの関数は、ロックが同時発生する時に、リソースが利用可能になった 協調型スレッドに通知を行うために用いる。これらは、pthreads
にある関数と よく似ている。しかしほとんどの目的において、キューの方がより単純で 直感的である。詳細はthreads::sharedを参照。
制御の明け渡し¶
There are times when you may find it useful to have a thread explicitly give up the CPU to another thread. You may be doing something processor-intensive and want to make sure that the user-interface thread gets called frequently. Regardless, there are times that you might want a thread to give up the processor.
あるスレッドが明示的に他のスレッドへとCPUを譲り渡せられたら便利だと 思うことがあるだろう。あなたはプロセッサ・インテンシブなことをしようと しているかもしれないし、ユーザーインターフェース担当のスレッドが呼び出される ことを確認したいと思うかもしれない。とにかく、スレッドがプロセッサを 明け渡せたらなあということがよくある。
Perl's threading package provides the yield() function that does this. yield() is pretty straightforward, and works like this:
Perlのスレッドパッケージはこれを実現するyield()関数を提供している。 yield()はとても素朴で、このように動作する:
use threads;
sub loop {
my $thread = shift;
my $foo = 50;
while($foo--) { print "in thread $thread\n" }
threads->yield;
$foo = 50;
while($foo--) { print "in thread $thread\n" }
}
my $thread1 = threads->new(\&loop, 'first');
my $thread2 = threads->new(\&loop, 'second');
my $thread3 = threads->new(\&loop, 'third');
It is important to remember that yield() is only a hint to give up the CPU, it depends on your hardware, OS and threading libraries what actually happens. On many operating systems, yield() is a no-op. Therefore it is important to note that one should not build the scheduling of the threads around yield() calls. It might work on your platform but it won't work on another platform.
yield()はCPUを明け渡すためのヒントでしかない。実際に何が起こるのかは、 ハードウェアやOS、そしてスレッドライブラリに依存している。 多くのオペレーティングシステムにおいて、yield()は何も機能しない。 それゆえ、yield()を呼んでスレッドのスケジューリングをたてるべきではない ことに注意しよう。あなたのプラットフォームでは動作したとしても、別の プラットフォームでは動かないかもしれないのだ。
General Thread Utility Routines¶
一般的なスレッドユーティリティルーチン¶
We've covered the workhorse parts of Perl's threading package, and with these tools you should be well on your way to writing threaded code and packages. There are a few useful little pieces that didn't really fit in anyplace else.
Perlのスレッドパッケージの働き部分をみてきた。これらの道具を使い、あなたの やり方でスレッドコードとパッケージを書いていくことができるはずだ。他の場所 では実際にはフィットしないが、便利な小物も少しはある。
私はどのスレッドにいる?¶
The threads->self
class method provides your program with a way to get an object representing the thread it's currently in. You can use this object in the same way as the ones returned from thread creation.
threads->self
クラスメソッドを使うと、現在のスレッドを表 すオブジェクトを得ることができる。スレッドを生成するときに返ってくる オブジェクトと同じように、このオブジェクトを使うことができる。
スレッドID¶
tid() is a thread object method that returns the thread ID of the thread the object represents. Thread IDs are integers, with the main thread in a program being 0. Currently Perl assigns a unique tid to every thread ever created in your program, assigning the first thread to be created a tid of 1, and increasing the tid by 1 for each new thread that's created.
tid()はオブジェクトが表すスレッドのIDを返すメソッドだ。スレッドIDは整数で、 プログラム中のメインスレッドは0である。現在のPerlは、プログラム中で生成された 全てのスレッドに一意なIDをつける。最初に生成されたスレッドには1があてられ、 新しいスレッドが作られる度にIDは1ずつ増えていく。
このスレッドは同じものか?¶
The equal() method takes two thread objects and returns true if the objects represent the same thread, and false if they don't.
equal()メソッドは二つのスレッドオブジェクトを引数にとり、オブジェクトが 同じスレッドを示していれば真を、そうでなければ偽を返す。
Thread objects also have an overloaded == comparison so that you can do comparison on them as you would with normal objects.
スレッドオブジェクトは比較演算子==をオーバーロードするので、普通の オブジェクトのようにそれらを比較することもできる。
どのスレッドが実行されているのか?¶
threads->list
returns a list of thread objects, one for each thread that's currently running and not detached. Handy for a number of things, including cleaning up at the end of your program:
threads->list
はスレッドオブジェクトのリストを返す。それぞれは 現在実行中で detachされていないスレッドである。プログラムの終わりに クリーンナップするのも含めていろいろな点で便利だ。
# 全部のスレッドを取り出しながらループ
foreach $thr (threads->list) {
# メインスレッドや自分自身は join しない
if ($thr->tid && !threads::equal($thr, threads->self)) {
$thr->join;
}
}
If some threads have not finished running when the main Perl thread ends, Perl will warn you about it and die, since it is impossible for Perl to clean up itself while other threads are running
もしメインスレッドが終了する時に、あるスレッドが実行を終了していなかったら、 Perlは警告を発し、die する。これは、他のスレッドが実行中の間はPerlが 自分自身をクリーンナップすることができないためだ。
完全な例¶
Confused yet? It's time for an example program to show some of the things we've covered. This program finds prime numbers using threads.
まだ混乱しているって?では、サンプルプログラムを使ってこれまで見てきたことの いくつかを示す時だ。このプログラムはスレッドを使って素数を見つけだす。
1 #!/usr/bin/perl -w
2 # prime-pthread, Tom Christiansen のご厚意による
3
4 use strict;
5
6 use threads;
7 use Thread::Queue;
8
9 my $stream = new Thread::Queue;
10 my $kid = new threads(\&check_num, $stream, 2);
11
12 for my $i ( 3 .. 1000 ) {
13 $stream->enqueue($i);
14 }
15
16 $stream->enqueue(undef);
17 $kid->join;
18
19 sub check_num {
20 my ($upstream, $cur_prime) = @_;
21 my $kid;
22 my $downstream = new Thread::Queue;
23 while (my $num = $upstream->dequeue) {
24 next unless $num % $cur_prime;
25 if ($kid) {
26 $downstream->enqueue($num);
27 } else {
28 print "Found prime $num\n";
29 $kid = new threads(\&check_num, $downstream, $num);
30 }
31 }
32 $downstream->enqueue(undef) if $kid;
33 $kid->join if $kid;
34 }
This program uses the pipeline model to generate prime numbers. Each thread in the pipeline has an input queue that feeds numbers to be checked, a prime number that it's responsible for, and an output queue into which it funnels numbers that have failed the check. If the thread has a number that's failed its check and there's no child thread, then the thread must have found a new prime number. In that case, a new child thread is created for that prime and stuck on the end of the pipeline.
このプログラムは素数を発生させるためにパイプラインモデルを利用している。 パイプラインにおけるそれぞれのスレッドは、素数を見つけるためにチェック される数を渡すインプット・キューと、チェックに失敗した数をかき集めておく アウトプット・キューとを持つ。チェックに失敗した数がスレッドにあり、かつ、 子スレッドがない場合、そのスレッドは新しい素数を見つけたことになる。 この場合、新しい子スレッドはその素数のために生成され、パイプラインの後ろに くっつけられる。
This probably sounds a bit more confusing than it really is, so let's go through this program piece by piece and see what it does. (For those of you who might be trying to remember exactly what a prime number is, it's a number that's only evenly divisible by itself and 1)
これは実際よりもわかりにくいかもしれない。だからこのプログラムを部分部分に わけて、なにをやっているのかみてみよう。 (素数とは正確には何っだたかということを思い出そうされている方のためにいうと、 自分自身と1でのみ割り切れる数のことだ。)
The bulk of the work is done by the check_num() subroutine, which takes a reference to its input queue and a prime number that it's responsible for. After pulling in the input queue and the prime that the subroutine's checking (line 20), we create a new queue (line 22) and reserve a scalar for the thread that we're likely to create later (line 21).
作業の大半はcheck_num()ルーチンによってなされる。このルーチンは インプット・キューへのリファレンスとスレッドが受け持つ素数を引数にとる。 [引数を]インプット・キューとサブルーチンがチェックする素数[のローカル変数]に 入れた後(20行目)、新しいキューを生成する(22行目)。そして、 後で生成するかもしれないスレッドを入れるスカラー変数を用意する(21行目)。
The while loop from lines 23 to line 31 grabs a scalar off the input queue and checks against the prime this thread is responsible for. Line 24 checks to see if there's a remainder when we modulo the number to be checked against our prime. If there is one, the number must not be evenly divisible by our prime, so we need to either pass it on to the next thread if we've created one (line 26) or create a new thread if we haven't.
23行目から31行目までの while ループでインプット・キューからスカラー値を 取り出し、このスレッドが受け持つ素数でチェックする。24行目では、チェック される数に対する法を行い、余りがあるかどうかを調べる。もしあれば、その数は 素数では割れないということだ。そこでその数を、すでに生成してあるのであれば 次のスレッドに渡すし、そうでなければ新しいスレッドをつくる。
The new thread creation is line 29. We pass on to it a reference to the queue we've created, and the prime number we've found.
29行目で新しいスレッドの生成を行っている。そのスレッドに、 先につくったキューへのリファレンスと発見された素数を渡す。
Finally, once the loop terminates (because we got a 0 or undef in the queue, which serves as a note to die), we pass on the notice to our child and wait for it to exit if we've created a child (lines 32 and 37).
最後に、(キューの中に 0 か undef があるかして)ループが終了したら、 子スレッドを作っていた場合には子スレッドに注意を促し[(undefを送る)]、 実行が終了するのを待つ(32,33行目[原文は37])。
Meanwhile, back in the main thread, we create a queue (line 9) and the initial child thread (line 10), and pre-seed it with the first prime: 2. Then we queue all the numbers from 3 to 1000 for checking (lines 12-14), then queue a die notice (line 16) and wait for the first child thread to terminate (line 17). Because a child won't die until its child has died, we know that we're done once we return from the join.
一方、メインスレッドに戻ってみると、キューを生成(9行目)、子スレッドを 初期化(10行目)、そして最初の素数 "2" を種としてセットする。 それからチェックするための3から1000までの数字をキューに入れる(12〜14行目)。 キューにdie通知[undef]を入れる(16行目)。そして最初の子スレッドが 終了するのを待つ(17行目)。ある子スレッドの子スレッドがdieするまで、 そのスレッドはdieしないので、joinから戻ってきたら作業が終わったことになる。
That's how it works. It's pretty simple; as with many Perl programs, the explanation is much longer than the program.
以上が仕組みだ。とても単純だ;多くのPerlプログラムにとってそうであるように、 説明の方が当のプログラム以上にとても長い。
様々なスレッドの実装¶
Some background on thread implementations from the operating system viewpoint. There are three basic categories of threads: user-mode threads, kernel threads, and multiprocessor kernel threads.
オペレーティングシステムの観点からみた、スレッド実装についての背景。 スレッドには三つの基本的なカテゴリーがある:ユーザーモードスレッド、 カーネルスレッド、そしてマルチプロセッサカーネルスレッドだ。
User-mode threads are threads that live entirely within a program and its libraries. In this model, the OS knows nothing about threads. As far as it's concerned, your process is just a process.
ユーザーモードスレッドとは、完全にプログラムとライブラリの中に存在する スレッドである。このモデルにおいては、OSはスレッドについて何も関知しない。 OSからしてみる限り、あなたのプロセスはただのひとつのプロセスである。
This is the easiest way to implement threads, and the way most OSes start. The big disadvantage is that, since the OS knows nothing about threads, if one thread blocks they all do. Typical blocking activities include most system calls, most I/O, and things like sleep().
これは最も簡単なスレッドの実装であり、ほとんどのOSが最初にとったやり方 である。この方法の大きな欠点は、OSがスレッドに関知していないために、 もしも一つのスレッドがブロックをしたら、全てのスレッドがブロックしてしまう ということだ。典型的なブロック行為には、ほとんどのシステムコール、ほとんどの I/O、そしてsleep()のようなものが含まれる。
Kernel threads are the next step in thread evolution. The OS knows about kernel threads, and makes allowances for them. The main difference between a kernel thread and a user-mode thread is blocking. With kernel threads, things that block a single thread don't block other threads. This is not the case with user-mode threads, where the kernel blocks at the process level and not the thread level.
カーネルスレッドは進化の第二段階だ。OSはカーネルスレッドについて知っていて、 その許可を出す。カーネルスレッドとユーザーモードスレッドの主要な違いは、 ブロッキングについてである。カーネルスレッドでは、一つのスレッドをブロック した事で他のスレッドまでブロックすることはない。これはユーザーモード スレッドにおいてはなりたたない。ユーザーモードでは、カーネルがブロックする のはプロセスレベルであって、スレッドレベルではないからだ。
This is a big step forward, and can give a threaded program quite a performance boost over non-threaded programs. Threads that block performing I/O, for example, won't block threads that are doing other things. Each process still has only one thread running at once, though, regardless of how many CPUs a system might have.
これは大きな前進であり、非スレッドプログラムに対し、スレッドプログラムが 大きなパフォーマンスの向上を得ることを可能にしている。例えば、I/Oの実行を ブロックするスレッドは、他のことを行うスレッドをブロックしないだろう。 しかし、システムがいくつCPUを備えているかに関係なく、それぞれのプロセスは いまだに一度に一つのスレッドしか走らせない。
Since kernel threading can interrupt a thread at any time, they will uncover some of the implicit locking assumptions you may make in your program. For example, something as simple as $a = $a + 2
can behave unpredictably with kernel threads if $a is visible to other threads, as another thread may have changed $a between the time it was fetched on the right hand side and the time the new value is stored.
カーネルのスレッド制御がいつでも割り込めるので、あなたがプログラム中に つくった暗黙のロックの前提が白日の下にさらけ出してしまうだろう。例えば、 $a = $a + 2
のような単純なものでも、$aが他のスレッドからみえるならば、 右辺の値を取り出す時間と新しい値を格納する時間の間に、他のスレッドが$aを 変えてしまって、あなたの予期しない振る舞いをしうる。
Multiprocessor kernel threads are the final step in thread support. With multiprocessor kernel threads on a machine with multiple CPUs, the OS may schedule two or more threads to run simultaneously on different CPUs.
マルチプロセッサカーネルスレッドは、スレッドのサポートにおける最終段階 である。一台のマシンが複数のCPUを持っているマルチプロセッサカーネルスレッド では、OSは別々のCPU上で同時に一つ以上のスレッドを走らせるようにスケジュール 管理をするだろう。
This can give a serious performance boost to your threaded program, since more than one thread will be executing at the same time. As a tradeoff, though, any of those nagging synchronization issues that might not have shown with basic kernel threads will appear with a vengeance.
一つ以上のスレッドが同時に実行するので、これによりスレッドプログラムは 飛躍的なパフォーマンスの向上を得ることができる。しかし、引き換えに、 基本的なカーネルスレッドでは現れなかったであろうイライラするような 同期性の問題が一気に姿を現す。
In addition to the different levels of OS involvement in threads, different OSes (and different thread implementations for a particular OS) allocate CPU cycles to threads in different ways.
スレッドを備えるOSの違いというレベルに加えて、それぞれのOSは(そして それぞれのスレッド実装は)それぞれの方法でもってCPUサイクルをスレッドに 割り当てる。
Cooperative multitasking systems have running threads give up control if one of two things happen. If a thread calls a yield function, it gives up control. It also gives up control if the thread does something that would cause it to block, such as perform I/O. In a cooperative multitasking implementation, one thread can starve all the others for CPU time if it so chooses.
次の二つのうちの一つが起こる場合、協調的マルチタスクシステムは実行中の スレッドに制御を明け渡させる。あるスレッドがyield関数を呼び出すと、制御を 明け渡す。また、スレッドがI/Oの実行などのブロックを引き起こすような何かを 行う場合も、制御を明け渡す。協調的マルチタスクシステムの実装においては、 そのように選択するならば、一つのスレッドが他の全てのスレッドのCPU時間を 食い尽くすことができる。
Preemptive multitasking systems interrupt threads at regular intervals while the system decides which thread should run next. In a preemptive multitasking system, one thread usually won't monopolize the CPU.
プリエンティブなマルチタスクシステムは、次にどのスレッドを実行するか 決めながら、一定の間隔でスレッドに割り込みをいれる。プリエンティブな マルチタスクシステムにおいて、通常一つのスレッドがCPUを独占するこはない。
On some systems, there can be cooperative and preemptive threads running simultaneously. (Threads running with realtime priorities often behave cooperatively, for example, while threads running at normal priorities behave preemptively.)
いくつかのシステムでは、協調的スレッドとプリエンティブなスレッドを同時に 実行することができる(例えば、通常のプライオリティを持ったスレッドが プリエンディブに振舞うのに対して、リアルタイムのプライオリティを持った スレッドはしばしば協調的に振舞う)。
Most modern operating systems support preemptive multitasking nowadays.
今では、ほとんどの近代的なOSでプリエンティブなマルチタスクをサポートしている。
パフォーマンスの考慮¶
The main thing to bear in mind when comparing ithreads to other threading models is the fact that for each new thread created, a complete copy of all the variables and data of the parent thread has to be taken. Thus thread creation can be quite expensive, both in terms of memory usage and time spent in creation. The ideal way to reduce these costs is to have a relatively short number of long-lived threads, all created fairly early on - before the base thread has accumulated too much data. Of course, this may not always be possible, so compromises have to be made. However, after a thread has been created, its performance and extra memory usage should be little different than ordinary code.
iスレッドと他のスレッドモデルを比較する際に忘れてならないのが、新しい スレッドはみな、親スレッドの変数とデータを全て完全にコピーして引き渡される という事実だ。そのため、メモリ使用量と実行時間の両方の点で、スレッドの生成は 非常に高くつく。このコストを減らす理想的な方法は、長生きなスレッドを 比較的少なめに持つことだ。このスレッドは全て、ベースとなるスレッドが非常に 多くのデータを蓄積する前に、適切に早い段階で生成される。もちろん、これは いつも可能というわけではないだろうから、妥協も必要だ。しかし、スレッドが 生成された後は、そのパフォーマンスと超過メモリー使用量は、普通のコードの それとほとんど違いは無い。
Also note that under the current implementation, shared variables use a little more memory and are a little slower than ordinary variables.
また、現在の実装下では、共有変数は通常の変数に比べて少し余計に メモリを使用し、スピードも少し遅いことに注意。
プロセススコープの変更¶
Note that while threads themselves are separate execution threads and Perl data is thread-private unless explicitly shared, the threads can affect process-scope state, affecting all the threads.
スレッドそれ自身は別々に実行され、Perlのデータは明示的に共有化しない限りは スレッド内でプライベートなものだ。その一方、スレッドは全てのスレッドに 影響を与えながら、プロセススコープの状態に影響を与えてしまうことに注意すること。
The most common example of this is changing the current working directory using chdir(). One thread calls chdir(), and the working directory of all the threads changes.
この最も一般的な例はchdir()を使ったときに現在作業中のディレクトリが 変更されてしまうことだ。一つのスレッドがchdir()を呼ぶと、全てのスレッドの 作業ディレクトリが変更される。
Even more drastic example of a process-scope change is chroot(): the root directory of all the threads changes, and no thread can undo it (as opposed to chdir()).
さらに劇的なプロセススコープの変更例はchroot()だ。全部のスレッドの ルートディレクトリが変更される。スレッドはそれを元に戻すことはできない (chdir()の方は可能)。
Further examples of process-scope changes include umask() and changing uids/gids.
さらなるプロセススコープ変化の例は、umask()とuid/gidの変更である。
Thinking of mixing fork() and threads? Please lie down and wait until the feeling passes-- but in case you really want to know, the semantics is that fork() duplicates all the threads. (In UNIX, at least, other platforms will do something different.)
fork()とスレッドを混在させたらどうなるかって?そんな気分が失せるまで、 横になって休んでいてください…。だが、本当に知りたいなら、fork()は全ての スレッドを複製するというのが、そのセマンティクスだ(少なくともUNIXの場合は。 他のプラットフォームでは違うことになるだろう)。
Similarly, mixing signals and threads should not be attempted. Implementations are platform-dependent, and even the POSIX semantics may not be what you expect (and Perl doesn't even give you the full POSIX API).
同じように、シグナルとスレッドを混ぜるのもやるべきではない。実装は プラットフォーム依存だし、POSIXのセマンティクスでさえ、あなたの期待通りには ならないかもしれない (そしてPerlはフルセットのPOSIX APIを提供することさえできない)。
システムライブラリにおけるスレッドの安全性¶
Whether various library calls are thread-safe is outside the control of Perl. Calls often suffering from not being thread-safe include: localtime(), gmtime(), get{gr,host,net,proto,serv,pw}*(), readdir(), rand(), and srand() -- in general, calls that depend on some global external state.
様々なライブラリの関数呼び出しがスレッドにとって安全かどうかということは、 Perlのコントロールの埒外である。しばしばスレッドセーフではないものに よって問題の生じる呼び出しには、localtime(), gmtime(), get{gr,host,net,proto,serv,pw}*(), readdir(),rand(), そしてsrand() -- 一般的にいって、グローバルな外部状況に依存する呼び出しが含まれる。
If the system Perl is compiled in has thread-safe variants of such calls, they will be used. Beyond that, Perl is at the mercy of the thread-safety or -unsafety of the calls. Please consult your C library call documentation.
Perlがコンパイルされたシステムが、そういった呼び出しのスレッドセーフな バージョンを持っているなら、そちらが使われる。それを超えると、Perlは 呼び出しがスレッドセーフかどうかのなすがままになる。Cライブラリの ドキュメントをよく吟味してほしい。
On some platforms the thread-safe library interfaces may fail if the result buffer is too small (for example the user group databases may be rather large, and the reentrant interfaces may have to carry around a full snapshot of those databases). Perl will start with a small buffer, but keep retrying and growing the result buffer until the result fits. If this limitless growing sounds bad for security or memory consumption reasons you can recompile Perl with PERL_REENTRANT_MAXSIZE defined to the maximum number of bytes you will allow.
いくつかのプラットフォームでは、結果バッファが小さすぎる場合に スレッドセーフなライブラリのインターフェースが失敗を起こすかもしれない (例えば、ユーザーグループのデータベースがかなり大きく、リエントラントな インターフェースがこれらのデータベースの完全なスナップショットをもたらさな ければならないような場合)。 Perlは小さなバッファでスタートする。しかし結果が適合するまで 結果バッファの確保を再試行し、大きくしうようとする。この無制限な成長が セキュリティやメモリ消費の理由から好ましくないものであるなら、 PERL_REENTRANT_MAXSIZEであなたの許す最大バイト数を定義してPerlを 再コンパイルすることができる。
おわりに¶
A complete thread tutorial could fill a book (and has, many times), but with what we've covered in this introduction, you should be well on your way to becoming a threaded Perl expert.
完全なスレッドのチュートリアルをつくると一冊の本になってしまう。 しかしこの導入でカバーしてきたことを使って、あなたなりの方法で スレッドPerlのエキスパートになっていってほしい。
参考文献¶
Here's a short bibliography courtesy of Jurgen Christoffel:
Jurgen Christoffelの御厚意による簡潔な参考文献集:
導入テキスト¶
Birrell, Andrew D. An Introduction to Programming with Threads. Digital Equipment Corporation, 1989, DEC-SRC Research Report #35 online as http://gatekeeper.dec.com/pub/DEC/SRC/research-reports/abstracts/src-rr-035.html (highly recommended)
Robbins, Kay. A., and Steven Robbins. Practical Unix Programming: A Guide to Concurrency, Communication, and Multithreading. Prentice-Hall, 1996.
Lewis, Bill, and Daniel J. Berg. Multithreaded Programming with Pthreads. Prentice Hall, 1997, ISBN 0-13-443698-9 (a well-written introduction to threads).
Nelson, Greg (editor). Systems Programming with Modula-3. Prentice Hall, 1991, ISBN 0-13-590464-1.
Nichols, Bradford, Dick Buttlar, and Jacqueline Proulx Farrell. Pthreads Programming. O'Reilly & Associates, 1996, ISBN 156592-115-1 (covers POSIX threads).
OS関連のリファレンス¶
Boykin, Joseph, David Kirschen, Alan Langerman, and Susan LoVerso. Programming under Mach. Addison-Wesley, 1994, ISBN 0-201-52739-1.
Tanenbaum, Andrew S. Distributed Operating Systems. Prentice Hall, 1995, ISBN 0-13-219908-4 (great textbook).
Silberschatz, Abraham, and Peter B. Galvin. Operating System Concepts, 4th ed. Addison-Wesley, 1995, ISBN 0-201-59292-4
その他のリファレンス¶
Arnold, Ken and James Gosling. The Java Programming Language, 2nd ed. Addison-Wesley, 1998, ISBN 0-201-31006-6.
comp.programming.threads FAQ, http://www.serpentine.com/~bos/threads-faq/
Le Sergent, T. and B. Berthomieu. "Incremental MultiThreaded Garbage Collection on Virtually Shared Memory Architectures" in Memory Management: Proc. of the International Workshop IWMM 92, St. Malo, France, September 1992, Yves Bekkers and Jacques Cohen, eds. Springer, 1992, ISBN 3540-55940-X (real-life thread applications).
Artur Bergman, "Where Wizards Fear To Tread", June 11, 2002, http://www.perl.com/pub/a/2002/06/11/threads.html
謝辞¶
Thanks (in no particular order) to Chaim Frenkel, Steve Fink, Gurusamy Sarathy, Ilya Zakharevich, Benjamin Sugars, Jurgen Christoffel, Joshua Pritikin, and Alan Burlison, for their help in reality-checking and polishing this article. Big thanks to Tom Christiansen for his rewrite of the prime number generator.
作者¶
Dan Sugalski <[email protected]<gt>
Slightly modified by Arthur Bergman to fit the new thread model/module.
新しいスレッドモデル・モジュールに対応するようにArthur Bergmanによって 若干の修正がなされた
Reworked slightly by Jorg Walter <[email protected]<gt> to be more concise about thread-safety of perl code.
perlコードのスレッドセーフティについてより簡明になるよう Jorg Walter <[email protected]<gt>によって改訂された。
Rearranged slightly by Elizabeth Mattijsen <[email protected]<gt> to put less emphasis on yield().
Elizabeth Mattijsen <[email protected]<gt>によって、yield()を以前より 強調しないよう若干アレンジされた。
著作権¶
The original version of this article originally appeared in The Perl Journal #10, and is copyright 1998 The Perl Journal. It appears courtesy of Jon Orwant and The Perl Journal. This document may be distributed under the same terms as Perl itself.
この記事の元版は The Perl Journal 10号 (copyright 1998 The Perl Journal) にある。Jon Orwant と The Perl Journal の厚意による。この文書の配布は Perlのそれと同じ条件のもとになされる。
For more information please see threads and threads::shared.
より詳細な情報はthreadsとthreads::sharedを参照。