16.2. threading
— 高水準のスレッドインタフェース¶
ソースコード: Lib/threading.py
このモジュールでは、高水準のスレッドインタフェースをより低水準な thread
モジュールの上に構築しています。 mutex
と Queue
モジュールのドキュメントも参照下さい。
また、 thread
がないために threading
を使えないような状況向けに dummy_threading
を提供しています。
注釈
Python 2.6 からこのモジュールは Java のスレッディング API の影響を受けた camelCase
のプロパティを置き換える PEP 8 に準拠したエイリアスを提供します。この更新された API は multiprocessing
モジュールのものと互換です。しかしながら、 camelCase
の名称の廃止の予定は決まっておらず、 Python 2.x と 3.x の両方でサポートされ続けます。
注釈
Python 2.5 から, 幾つかの Thread のメソッドは間違った呼び出しに対して AssertionError
の代わりに RuntimeError
を返します。
CPython 実装の詳細: CPython は Global Interpreter Lock のため、ある時点で Python コードを実行できるスレッドは1つに限られます (ただし、いくつかのパフォーマンスが強く求められるライブラリはこの制限を克服しています)。アプリケーションにマルチコアマシンの計算能力をより良く利用させたい場合は、 multiprocessing
モジュールの利用をお勧めします。 ただし、I/Oバウンドなタスクを並行して複数走らせたい場合においては、 マルチスレッドは正しい選択肢です。
このモジュールでは以下のような関数とオブジェクトを定義しています:
-
threading.
active_count
()¶ -
threading.
activeCount
()¶ 生存中の
Thread
オブジェクトの数を返します。この数はenumerate()
の返すリストの長さと同じです。バージョン 2.6 で変更: 新たに
active_count()
として使えるようになりました。
-
threading.
Condition
() 新しい条件変数 (condition variable) オブジェクトを返すファクトリ関数です。条件変数を使うと、ある複数のスレッドを別のスレッドの通知があるまで待機させられます。
Condition オブジェクト を参照してください。
-
threading.
current_thread
()¶ -
threading.
currentThread
()¶ 関数を呼び出している処理のスレッドに対応する
Thread
オブジェクトを返します。関数を呼び出している処理のスレッドがthreading
モジュールで生成したものでない場合、限定的な機能しかもたないダミースレッドオブジェクトを返します。バージョン 2.6 で変更: 新たに
current_thread()
として使えるようになりました。
-
threading.
enumerate
()¶ 現在、生存中の
Thread
オブジェクト全てのリストを返します。リストには、デーモンスレッド (daemonic thread)、current_thread()
の生成するダミースレッドオブジェクト、そして主スレッドが入ります。終了したスレッドとまだ開始していないスレッドは入りません。
-
threading.
Event
() 新たなイベントオブジェクトを返すファクトリ関数です。イベントは
set()
メソッドを使うとTrue
に、clear()
メソッドを使うとFalse
にセットされるようなフラグを管理します。wait()
メソッドは、全てのフラグが真になるまでブロックするようになっています。Event オブジェクト を参照してください。
-
class
threading.
local
¶ スレッドローカルデータ (thread-local data) を表現するためのクラスです。スレッドローカルデータとは、値が各スレッド固有になるようなデータです。スレッドローカルデータを管理するには、
local
(またはlocal
のサブクラス) のインスタンスを作成して、その属性に値を代入しますmydata = threading.local() mydata.x = 1
インスタンスの値はスレッドごとに違った値になります。
詳細と例題については、
_threading_local
モジュールのドキュメンテーション文字列を参照してください。バージョン 2.4 で追加.
-
threading.
Lock
()¶ 新しいプリミティブロック (primitive lock) オブジェクトを返すファクトリ関数です。スレッドが一度プリミティブロックを獲得すると、それ以後のロック獲得の試みはロックが解放されるまでブロックします。どのスレッドでもロックを解放できます。
Lock オブジェクト を参照してください。
-
threading.
RLock
()¶ 新しい再入可能ロックオブジェクトを返すファクトリ関数です。再入可能ロックはそれを獲得したスレッドによって解放されなければなりません。いったんスレッドが再入可能ロックを獲得すると、同じスレッドはブロックされずにもう一度それを獲得できます ; そのスレッドは獲得した回数だけ解放しなければいけません。
RLock オブジェクト を参照してください。
-
threading.
Semaphore
([value]) 新しいセマフォ (semaphore) オブジェクトを返すファクトリ関数です。セマフォは、
release()
を呼び出した数からacquire()
を呼び出した数を引き、初期値を足した値を表すカウンタを管理します。acquire()
メソッドは、カウンタの値を負にせずに処理を戻せるまで必要ならば処理をブロックします。 value を指定しない場合、デフォルトの値は 1 になります。Semaphore オブジェクト を参照してください。
-
threading.
BoundedSemaphore
([value])¶ 新しい有限セマフォ (bounded semaphore) オブジェクトを返すファクトリ関数です。有限セマフォは、現在の値が初期値を超過しないようチェックを行います。超過を起こした場合、
ValueError
を送出します。たいていの場合、セマフォは限られた容量のリソースを保護するために使われるものです。従って、あまりにも頻繁なセマフォの解放はバグが生じているしるしです。 value を指定しない場合、デフォルトの値は 1 になります。
-
class
threading.
Thread
処理中のスレッドを表すクラスです。このクラスは制限のある範囲内で安全にサブクラス化できます。
Thread オブジェクト を参照してください。
-
class
threading.
Timer
指定時間経過後に関数を実行するスレッドです。
Timer オブジェクト を参照してください。
-
threading.
settrace
(func)¶ threading
モジュールを使って開始した全てのスレッドにトレース関数を設定します。 func は各スレッドのrun()
を呼び出す前にスレッドのsys.settrace()
に渡されます。バージョン 2.3 で追加.
-
threading.
setprofile
(func)¶ threading
モジュールを使って開始した全てのスレッドにプロファイル関数を設定します。 func は各スレッドのrun()
を呼び出す前にスレッドのsys.setprofile()
に渡されます。バージョン 2.3 で追加.
-
threading.
stack_size
([size])¶ 新しいスレッドを作るときのスレッドスタックサイズを返します。オプションの size 引数は、これ以降に作成するスレッドのスタックサイズを指定するもので、 0 (プラットフォームか設定されたデフォルト値) か、 32,768 (32 KiB) 以上の正の整数である必要があります。 size を指定しない場合、0 が使われます。スレッドのスタックサイズの変更がサポートされていない場合、
ThreadError
を発生させます。不正なスタックサイズが指定された場合、ValueError
を発生させて、スタックサイズを変更しません。 32 KiB が現在のインタープリター自体のために十分であると保証された最小のスタックサイズです。いくつかのプラットフォームではスタックサイズに対して制限があることに注意してください。例えば最小のスタックサイズが 32 KiB より大きかったり、システムのメモリページサイズ の整数倍の必要があるなどです。この制限についてはプラットフォームのドキュメントを参照する必要があります。 (一般的なページサイズは 4 KiB なので、プラットフォームに関する情報がない場合は 4096 の整数倍のスタックサイズを選ぶといいかもしれません)。利用可能な環境: Windows, POSIX スレッドに対応したシステム。バージョン 2.5 で追加.
-
exception
threading.
ThreadError
¶ 下記で記述するスレッド関連の様々なエラーで送出されます。多くのインターフェイスが
ThreadError
ではなくRuntimeError
を使っていることに注意してください。
オブジェクトの詳細なインターフェースを以下に説明します。
このモジュールのおおまかな設計は Java のスレッドモデルに基づいています。とはいえ、 Java がロックと条件変数を全てのオブジェクトの基本的な挙動にしているのに対し、 Python ではこれらを別個のオブジェクトに分けています。 Python の Thread
クラスがサポートしているのは Java の Thread クラスの挙動のサブセットにすぎません; 現状では、優先度 (priority)やスレッドグループがなく、スレッドの破壊 (destroy)、中断 (stop)、一時停止 (suspend)、復帰 (resume)、割り込み (interrupt) は行えません。 Java の Thread クラスにおける静的メソッドに対応する機能が実装されている場合にはモジュールレベルの関数になっています。
以下に説明するメソッドは全て原子的 (atomic) に実行されます。
16.2.1. Thread オブジェクト¶
このクラスは個別のスレッド中で実行される活動 (activity) を表現します。活動を決める方法は 2 つあり、一つは呼出し可能オブジェクトをコンストラクタへ渡す方法、もう一つはサブクラスで run()
メソッドをオーバライドする方法です。 (コンストラクタを除く) その他のメソッドは一切サブクラスでオーバライドしてはなりません。言い換えるならば、このクラスの __init__()
と run()
メソッド だけ をオーバライドしてくださいということです。
ひとたびスレッドオブジェクトを生成すると、スレッドの start()
メソッドを呼び出して活動を開始せねばなりません。 start()
メソッドはそれぞれのスレッドの run()
メソッドを起動します。
スレッドの活動が始まると、スレッドは '生存中 (alive)' とみなされます。スレッドは通常 run()
メソッドが終了するまで生存中となります。もしくは、捕捉されない例外が送出されるまでです。 is_alive()
メソッドはスレッドが生存中であるかどうか調べます。
他のスレッドはスレッドの join()
メソッドを呼び出せます。このメソッドは、 join()
を呼び出されたスレッドが終了するまで、メソッドの呼び出し手となるスレッドをブロックします。
スレッドには名前があります。名前はコンストラクタに渡したり、または、 name
属性を通して読み出したり、変更したりできます。
スレッドには "デーモンスレッド (daemon thread)" であるというフラグを立てられます。このフラグには、残っているスレッドがデーモンスレッドだけになった時に Python プログラム全体を終了させるという意味があります。フラグの初期値はスレッドを生成する側のスレッドから継承します。フラグの値は daemon
属性を通して設定できます。
注釈
デーモンスレッドは終了時にいきなり停止されます。デーモンスレッドで使われたリソース (開いているファイル、データベースのトランザクションなど) は適切に解放されないかもしれません。きちんと (gracefully) スレッドを停止したい場合は、スレッドを非デーモンスレッドにして、Event
のような適切なシグナル送信機構を使用してください。
スレッドには "主スレッド (main thread)" オブジェクトがあります。主スレッドは Python プログラムを最初に制御していたスレッドです。主スレッドはデーモンスレッドではありません。
"ダミースレッド (dummy thread)" オブジェクトを作成できる場合があります。ダミースレッドは、 "外来スレッド (alien thread)" に相当するスレッドオブジェクトです。ダミースレッドは、C コードから直接生成されたスレッドのような、 threading
モジュールの外で開始された処理スレッドです。ダミースレッドオブジェクトには限られた機能しかなく、常に生存中、かつデーモンスレッドであるとみなされ、 join()
できません。また、外来スレッドの終了を検出するのは不可能なので、ダミースレッドは削除できません。
-
class
threading.
Thread
(group=None, target=None, name=None, args=(), kwargs={})¶ コンストラクタは常にキーワード引数を使って呼び出さなければなりません。各引数は以下の通りです:
group は
None
でなければなりません。将来ThreadGroup
クラスが実装されたときの拡張用に予約されている引数です。target は
run()
メソッドによって起動される呼び出し可能オブジェクトです。デフォルトでは何も呼び出さないことを示すNone
になっています。name はスレッドの名前です。デフォルトでは、 N を小さな 10 進数として、 "Thread- N" という形式の一意な名前を生成します。
args は target を呼び出すときの引数タプルです。デフォルトは
()
です。kwargs は target を呼び出すときのキーワード引数の辞書です。デフォルトは
{}
です。サブクラスでコンストラクタをオーバライドした場合、必ずスレッドが何かを始める前に基底クラスのコンストラクタ (
Thread.__init__()
) を呼び出しておかなくてはなりません。-
start
()¶ スレッドの活動を開始します。
このメソッドは、スレッドオブジェクトあたり一度しか呼び出してはなりません。
start()
は、オブジェクトのrun()
メソッドが個別の処理スレッド中で呼び出されるように調整します。同じスレッドオブジェクトに対し、このメソッドを2回以上呼び出した場合、
RuntimeError
を送出します。
-
run
()¶ スレッドの活動をもたらすメソッドです。
このメソッドはサブクラスでオーバライドできます。標準の
run()
メソッドでは、オブジェクトのコンストラクタの target 引数に呼び出し可能オブジェクトを指定した場合、 args および kwargs の引数列およびキーワード引数とともに呼び出します。
-
join
([timeout])¶ スレッドが終了するまで待機します。このメソッドは、
join()
を呼び出されたスレッドが、正常終了あるいは処理されない例外によって終了するか、オプションのタイムアウトが発生するまで、メソッドの呼び出し手となるスレッドをブロックします。timeout 引数を指定して、
None
以外の値にする場合、タイムアウトを秒 (または端数秒) を表す浮動小数点数でなければなりません。join()
はいつでもNone
を返すので、isAlive()
を呼び出してタイムアウトしたかどうかを確認しなければなりません。もしスレッドがまだ生存中であれば、join()
はタイムアウトしています。timeout が指定されないかまたは
None
であるときは、この操作はスレッドが終了するまでブロックします。一つのスレッドに対して何度でも
join()
できます。実行中のスレッドに対し、
join()
を呼び出そうとすると、デッドロックを引き起こすため、RuntimeError
が送出されます。スレッドが開始される前にjoin()
を呼び出そうとしても、同じ例外が送出されます。
-
name
¶ 識別のためにのみ用いられる文字列です。名前には機能上の意味づけ (semantics) はありません。複数のスレッドに同じ名前をつけてもかまいません。名前の初期値はコンストラクタで設定されます。
バージョン 2.6 で追加.
-
ident
¶ 'スレッド識別子' 、または、スレッドが開始されていなければ
None
です。非ゼロの整数です。thread.get_ident()
関数を参照下さい。スレッド識別子は、スレッドが終了した後、新たなスレッドが生成された場合、再利用され得ます。スレッド識別子は、スレッドが終了した後でも利用できます。バージョン 2.6 で追加.
-
is_alive
()¶ -
isAlive
()¶ スレッドが生存中かどうかを返します。
このメソッドは
run()
メソッドが起動した直後からそのrun()
メソッドが終了するまでの間True
を返します。モジュール関数、enumerate()
は、全ての生存中のスレッドのリストを返します。バージョン 2.6 で変更: 新たに
is_alive()
として使えるようになりました。
-
daemon
¶ スレッドのデーモンフラグです。このフラグは
start()
の呼び出し前に設定されなければなりません。さもなくば、RuntimeError
が送出されます。初期値は生成側のスレッドから継承されます。メインスレッドはデーモンスレッドではないので、メインスレッドから生成するスレッドはデフォルトでdaemon
=False
です。デーモンでない生存中のスレッドが全てなくなると、 Python プログラム全体が終了します。
バージョン 2.6 で追加.
-
16.2.2. Lock オブジェクト¶
プリミティブロックとは、ロックが生じた際に特定のスレッドによって所有されない同期プリミティブです。 Python では現在のところ拡張モジュール thread
で直接実装されている最も低水準の同期プリミティブを使えます。
プリミティブロックは2つの状態、 "ロック" または "アンロック" があります。このロックはアンロック状態で作成されます。ロックには基本となる二つのメソッド、 acquire()
と release()
があります。ロックの状態がアンロックである場合、 acquire()
は状態をロックに変更して即座に処理を戻します。状態がロックの場合、 acquire()
は他のスレッドが release()
を呼出してロックの状態をアンロックに変更するまでブロックします。その後、状態をロックに再度設定してから処理を戻します。 release()
メソッドを呼び出すのはロック状態のときでなければなりません; このメソッドはロックの状態をアンロックに変更し、即座に処理を戻します。アンロックの状態のロックを解放しようとすると ThreadError
が送出されます。
複数のスレッドにおいて acquire()
がアンロック状態への遷移を待っているためにブロックが起きている時に release()
を呼び出してロックの状態をアンロックにすると、一つのスレッドだけが処理を進行できます。どのスレッドが処理を進行できるのかは定義されておらず、実装によって異なるかもしれません。
全てのメソッドはアトミックに実行されます。
-
Lock.
acquire
([blocking])¶ ブロックあり、またはブロックなしでロックを獲得します。
引数 blocking を
True
(デフォルト) に設定して呼び出した場合、ロックがアンロック状態になるまでブロックします。そしてそれをロック状態にしてからTrue
を返します。引数 blocking の値を
False
にして呼び出すとブロックしません。blocking をTrue
にして呼び出した場合にブロックするような状況では、直ちにFalse
を返します。それ以外の場合には、ロックをロック状態にしてTrue
を返します。
-
Lock.
release
()¶ ロックを解放します。
ロックの状態がロックのとき、状態をアンロックにリセットして処理を戻します。他のスレッドがロックがアンロック状態になるのを待ってブロックしている場合、ただ一つのスレッドだけが処理を継続できるようにします。
アンロック状態のロックに対して起動された場合、
ThreadError
が送出されます。戻り値はありません。
16.2.3. RLock オブジェクト¶
再入可能ロック (reentrant lock) とは、同じスレッドが複数回獲得できるような同期プリミティブです。再入可能ロックの内部では、プリミティブロックの使うロック/アンロック状態に加え、 "所有スレッド (owning thread)" と "再帰レベル (recursion level)" という概念を用いています。ロック状態では何らかのスレッドがロックを所有しており、アンロック状態ではいかなるスレッドもロックを所有していません。
スレッドがこのロックの状態をロックにするには、ロックの acquire()
メソッドを呼び出します。このメソッドは、スレッドがロックを所有すると処理を戻します。ロックの状態をアンロックにするには release()
メソッドを呼び出します。 acquire()
/ release()
からなるペアの呼び出しはネストできます; 最後に呼び出した release()
(最も外側の呼び出しペア) だけが、ロックの状態をアンロックにリセットし、 acquire()
でブロック中の別のスレッドの処理を進行させられます。
-
RLock.
acquire
([blocking=1])¶ ブロックあり、またはブロックなしでロックを獲得します。
引数なしで呼び出した場合: スレッドが既にロックを所有している場合、再帰レベルをインクリメントして即座に処理を戻します。それ以外の場合、他のスレッドがロックを所有していれば、そのロックの状態がアンロックになるまでブロックします。その後、ロックの状態がアンロックになる (いかなるスレッドもロックを所有しない状態になる) と、ロックの所有権を獲得し、再帰レベルを 1 にセットして処理を戻します。ロックの状態がアンロックになるのを待っているスレッドが複数ある場合、その中の一つだけがロックの所有権を獲得できます。この場合、戻り値はありません。
引数 blocking の値を true にして呼び出した場合、引数なしで呼び出したときと同じことを行ない、true を返します。
引数 blocking の値を false にして呼び出すとブロックしません。引数なしで呼び出した場合にブロックするような状況であった場合には直ちに false を返します。それ以外の場合には、引数なしで呼び出したときと同じ処理を行い true を返します。
-
RLock.
release
()¶ 再帰レベルをデクリメントしてロックを解放します。デクリメント後に再帰レベルがゼロになった場合、ロックの状態をアンロック (いかなるスレッドにも所有されていない状態) にリセットし、ロックの状態がアンロックになるのを待ってブロックしているスレッドがある場合にはその中のただ一つだけが処理を進行できるようにします。デクリメント後も再帰レベルがゼロでない場合、ロックの状態はロックのままで、呼び出し側のスレッドに所有されたままになります。
呼び出し側のスレッドがロックを所有しているときにのみこのメソッドを呼び出してください。ロックの状態がアンロックの時にこのメソッドを呼び出すと、
RuntimeError
が送出されます。戻り値はありません。
16.2.4. Condition オブジェクト¶
条件変数 (condition variable) は常にある種のロックに関連付けられています; 条件変数に関連付けるロックは明示的に引き渡したり、デフォルトで生成させたりできます。 (複数の条件変数で同じロックを共有するような場合には、引渡しによる関連付けが便利です。)
条件変数には、 acquire()
メソッドおよび release()
があり、関連付けされているロックの対応するメソッドを呼び出すようになっています。また、 wait()
, notify()
, notifyAll()
といったメソッドがあります。これら三つのメソッドを呼び出せるのは、呼び出し手のスレッドがロックを獲得している時だけです。そうでない場合は RuntimeError
が送出されます。
wait()
メソッドは現在のスレッドのロックを解放し、他のスレッドが同じ条件変数に対して notify()
または notifyAll()
を呼び出して現在のスレッドを起こすまでブロックします。一度起こされると、再度ロックを獲得して処理を戻します。 wait()
にはタイムアウトも設定できます。
notify()
メソッドは条件変数待ちのスレッドを1つ起こします。 notifyAll()
メソッドは条件変数待ちの全てのスレッドを起こします。
注意: notify()
と notifyAll()
はロックを解放しません; 従って、スレッドが起こされたとき、 wait()
の呼び出しは即座に処理を戻すわけではなく、 notify()
または notifyAll()
を呼び出したスレッドが最終的にロックの所有権を放棄したときに初めて処理を返すのです。
豆知識: 条件変数を使う典型的なプログラミングスタイルでは、何らかの共有された状態変数へのアクセスを同期させるためにロックを使います; 状態変数が特定の状態に変化したことを知りたいスレッドは、自分の望む状態になるまで繰り返し wait()
を呼び出します。その一方で、状態変更を行うスレッドは、前者のスレッドが待ち望んでいる状態であるかもしれないような状態へ変更を行ったときに notify()
や notifyAll()
を呼び出します。例えば、以下のコードは無制限のバッファ容量のときの一般的な生産者-消費者問題です:
# Consume one item
cv.acquire()
while not an_item_is_available():
cv.wait()
get_an_available_item()
cv.release()
# Produce one item
cv.acquire()
make_an_item_available()
cv.notify()
cv.release()
notify()
と notifyAll()
のどちらを使うかは、その状態の変化に興味を持っている待ちスレッドが一つだけなのか、あるいは複数なのかで考えます。例えば、典型的な生産者-消費者問題では、バッファに 1 つの要素を加えた場合には消費者スレッドを 1 つしか起こさなくてかまいません。
-
class
threading.
Condition
([lock])¶ lock を指定して、
None
の値にする場合、Lock
またはRLock
オブジェクトでなければなりません。この場合、 lock は根底にあるロックオブジェクトとして使われます。それ以外の場合には新しいRLock
オブジェクトを生成して使います。-
acquire
(*args)¶ 根底にあるロックを獲得します。このメソッドは根底にあるロックの対応するメソッドを呼び出します。そのメソッドの戻り値を返します。
-
release
()¶ 根底にあるロックを解放します。このメソッドは根底にあるロックの対応するメソッドを呼び出します。戻り値はありません。
-
wait
([timeout])¶ 通知 (notify) を受けるか、タイムアウトするまで待機します。呼び出し側のスレッドがロックを獲得していないときにこのメソッドを呼び出すと
RuntimeError
が送出されます。このメソッドは根底にあるロックを解放し、他のスレッドが同じ条件変数に対して
notify()
またはnotifyAll()
を呼び出して現在のスレッドを起こすか、オプションのタイムアウトが発生するまでブロックします。一度スレッドが起こされると、再度ロックを獲得して処理を戻します。timeout 引数を指定して、
None
以外の値にする場合、タイムアウトを秒 (または端数秒) を表す浮動小数点数でなければなりません。根底にあるロックが
RLock
である場合、release()
メソッドではロックは解放されません。というのも、ロックが再帰的に複数回獲得されている場合には、release()
によって実際にアンロックが行われないかもしれないからです。その代わり、ロックが再帰的に複数回獲得されていても確実にアンロックを行えるRLock
クラスの内部インタフェースを使います。その後ロックを再獲得する時に、もう一つの内部インタフェースを使ってロックの再帰レベルを復帰します。
-
notify
(n=1)¶ デフォルトで、この条件変数を待っている1つのスレッドを起こします。 呼び出し側のスレッドがロックを獲得していないときにこのメソッドを呼び出すと
RuntimeError
が送出されます。何らかの待機中スレッドがある場合、そのうち n スレッドを起こします。待機中のスレッドがなければ何もしません。
現在の実装では、少なくとも n スレッドが待機中であれば、ちょうど n スレッドを起こします。とはいえ、この挙動に依存するのは安全ではありません。将来、実装の最適化によって、複数のスレッドを起こすようになるかもしれないからです。
注意: 起こされたスレッドは実際にロックを再獲得できるまで
wait()
呼び出しから戻りません。notify()
はロックを解放しないので、notify()
呼び出し側は明示的にロックを解放しなければなりません。
-
notify_all
()¶ -
notifyAll
()¶ この条件を待っているすべてのスレッドを起こします。このメソッドは
notify()
のように動作しますが、 1 つではなくすべての待ちスレッドを起こします。呼び出し側のスレッドがロックを獲得していない場合、RuntimeError
が送出されます。バージョン 2.6 で変更: 新たに
notify_all()
として使えるようになりました。
-
16.2.5. Semaphore オブジェクト¶
セマフォ (semaphore) は、計算機科学史上最も古い同期プリミティブの一つで、草創期のオランダ計算機科学者 Edsger W. Dijkstra によって発明されました (彼は acquire()
と release()
の代わりに P()
と V()
を使いました)。
セマフォは acquire()
でデクリメントされ release()
でインクリメントされるような内部カウンタを管理します。カウンタは決してゼロより小さくはなりません; acquire()
は、カウンタがゼロになっている場合、他のスレッドが release()
を呼び出すまでブロックします。
-
class
threading.
Semaphore
([value])¶ オプションの引数には、内部カウンタの初期値を指定します。デフォルトは
1
です。与えられた value が 0 より小さい場合、ValueError
が送出されます。-
acquire
([blocking])¶ セマフォを獲得します。
引数なしで呼び出した場合:
acqure()
処理に入ったときに内部カウンタがゼロより大きければ、カウンタを 1 デクリメントして即座に処理を戻します。acqure()
処理に入ったときに内部カウンタがゼロの場合、他のスレッドがrelease()
を呼び出してカウンタをゼロより大きくするまでブロックします。この処理は、適切なインターロック (interlock) を介して行い、複数のacquire()
呼び出しがブロックされた場合、release()
が正確に一つだけを起こせるようにします。この実装はランダムに一つ選択するだけでもよいので、ブロックされたスレッドがどの起こされる順番に依存してはなりません。この場合、戻り値はありません。blocking 引数の値を真にした場合、引数なしで呼び出した場合と同じ処理を行って真を返します。
blocking を false にして呼び出すとブロックしません。引数なしで呼び出した場合にブロックするような状況であった場合には直ちに false を返します。それ以外の場合には、引数なしで呼び出したときと同じ処理を行い true を返します。
-
release
()¶ 内部カウンタを 1 インクリメントして、セマフォを解放します。
release()
処理に入ったときにカウンタがゼロであり、カウンタの値がゼロより大きくなるのを待っている別のスレッドがあった場合、そのスレッドを起こします。
-
16.2.5.1. Semaphore
の例¶
セマフォはしばしば、容量に限りのある資源、例えばデータベースサーバなどを保護するために使われます。リソースが固定の状況では、常に有限セマフォを使わなければなりません。主スレッドは、作業スレッドを立ち上げる前にセマフォを初期化します:
maxconnections = 5
...
pool_sema = BoundedSemaphore(value=maxconnections)
作業スレッドは、ひとたび立ち上がると、サーバへ接続する必要が生じたときにセマフォの acquire()
および release()
メソッドを呼び出します:
pool_sema.acquire()
conn = connectdb()
... use connection ...
conn.close()
pool_sema.release()
有限セマフォを使うと、セマフォを獲得回数以上に解放してしまうというプログラム上の間違いを見逃しにくくします。
16.2.6. Event オブジェクト¶
イベントは、あるスレッドがイベントを発信し、他のスレッドはそれを待つという、スレッド間で通信を行うための最も単純なメカニズムの一つです。
イベントオブジェクトは内部フラグを管理します。このフラグは set()
メソッドで値を true に、 clear()
メソッドで値を false にリセットします。 wait()
メソッドはフラグが true になるまでブロックします。
-
class
threading.
Event
¶ 内部フラグの初期値は偽です。
-
set
()¶ 内部フラグの値を true にセットします。フラグの値が true になるのを待っている全てのスレッドを起こします。一旦フラグが true になると、スレッドが
wait()
を呼び出しても全くブロックしなくなります。
-
clear
()¶ 内部フラグの値を false にリセットします。以降は、
set()
を呼び出して再び内部フラグの値を true にセットするまで、wait()
を呼び出したスレッドはブロックするようになります。
-
wait
([timeout])¶ 内部フラグの値が true になるまでブロックします。
wait()
処理に入った時点で内部フラグの値が true であれば、直ちに処理を戻します。そうでない場合、他のスレッドがset()
を呼び出してフラグの値を true にセットするか、オプションのタイムアウトが発生するまでブロックします。timeout 引数を指定して、
None
以外の値にする場合、タイムアウトを秒 (または端数秒) を表す浮動小数点数でなければなりません。このメソッドは終了時の内部フラグを返します。 timeout が指定されて、操作がタイムアウトしたとき以外は、
True
を返すはずです。バージョン 2.7 で変更: 以前は、このメソッドは常に
None
を返していました。
-
16.2.7. Timer オブジェクト¶
このクラスは、一定時間経過後に実行される活動、すなわちタイマ活動を表現します。 Timer
は Thread
のサブクラスであり、自作のスレッドを構築した一例でもあります。
タイマは start()
メソッドを呼び出すとスレッドとして作動し始めします。 (活動を開始する前に) cancel()
メソッドを呼び出すと、タイマを停止できます。タイマが活動を実行するまでの待ち時間は、ユーザが指定した待ち時間と必ずしも厳密には一致しません。
例えば:
def hello():
print "hello, world"
t = Timer(30.0, hello)
t.start() # after 30 seconds, "hello, world" will be printed
16.2.8. with
文でのロック・条件変数・セマフォの使い方¶
このモジュールのオブジェクトで acquire()
と release()
両メソッドを具えているものは全て with
文のコンテキストマネージャとして使うことができます。 acquire()
メソッドが with
文のブロックに入るときに呼び出され、ブロック脱出時には release()
メソッドが呼ばれます。
現在のところ、 Lock
、 RLock
、 Condition
、 Semaphore
、 BoundedSemaphore
を with
文のコンテキストマネージャとして使うことができます。以下の例を見てください。
import threading
some_rlock = threading.RLock()
with some_rlock:
print "some_rlock is locked while this executes"
16.2.9. スレッド化されたコード中でのImport¶
スレッドセーフなimportのためには、継承の制限に起因する、ふたつの重要な制約があります。
- ひとつ目は、主とするモジュール以外では、importが新しいスレッドを生成しないようになっていなければなりません。そして、そのスレッドを待たなければなりません。この制約を守らない場合、生成されたスレッドが直接的、または、間接的にモジュールをimportしようとした際に、デッドロックを引き起こす可能性があります。
- ふたつ目は、全てのimportが、インタープリターが自身を終了させる前に完了しなければなりません。これは、最も簡単な方法としては、threadingモジュールを通して生成される非デーモンからのみimportを実行することで達成できます。デーモンスレッド、および、直接、threadモジュールから生成されたスレッドは、インタープリター終了後にimportを実行しないようにする、別の同期の仕組みを必要とします。この制約を守らない場合、 intermittent (間歇) 例外を引き起こし、インタープリターのシャットダウン中にクラッシュする可能性があります。 (後から実行されるimportは、すでにアクセス可能でなくなった領域にアクセスしようとするためです)