7.5.3 Condition オブジェクト

条件変数(condition variable)はつねにある種類のロックに関係しています; 明示的にロックを渡すこともできますし、 デフォルトによって暗黙で作成させることもできます。 (いくつかの条件変数が同じロックを共有しなければならない場合、 そのロックを明示的に渡すことができます。)

条件変数は、関連するロックと対応するメソッドを呼出すacquire()release()を持っています。 加えてwait(),notify(),notifyAll()メソッドを 持っています。 呼出しスレッドがロックを獲得しているときにだけ、 この3つのメソッドを呼出すようにしなければいけません。

wait()メソッドはロックを解放し、他のスレッドで同じ条件変数を notify()またはnotifyAll()によって起こされるまで ブロックします。 いったん起こされるとそれはロックを再獲得して戻ります。 タイムアウトを指定することも可能です。

notify()メソッドは条件変数を待っているスレッドを1つ起こします。 notifyAll()メソッドは条件変数を待っている全てのスレッドを起こします。

Note: notify()notifyAll()はロックを解放しません。 これは次のことを意味します; スレッドが起こされたときwait()呼出しから直ちに戻らず、 notify()またはnotifyAll()を呼出したスレッドが 最終的にロックの所有権を譲ることで戻ります。

Tip: 条件変数を使う典型的なプログラミングスタイルは、 共有状態へ同期アクセスするためにロックを使います。 状態の特定の変更に興味のあるスレッドは、望む状態になるまでwait()を 繰り返し呼出します。 同時に、待ちスレッドのうちの1つが望む状態に対して、 状態を変更するスレッドが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つのアイテムを加えたとき、 1つだけ消費者スレッドを起こす必要があります。

class Condition( [lock])
もしlock引数をNone以外で与えるならば、 LockまたはRLockオブジェクトでなければならず、 それは基礎ロックとして使われます。 それ以外のとき、新しいRLockオブジェクトを作り基礎ロックとして 使われます。

acquire( *args)
基礎ロックを獲得します。 このメソッドは基礎ロックに関連するメソッドを呼出します。 返り値はそのメソッドからのものです。

release( )
基礎ロックを解放します。 このメソッドは基礎ロックに関連するメソッドを呼出します。 返り値はありません。

wait( [timeout])
通知を受ける(notify)あるいはタイムアウトするまで待ちます。 これは呼出しスレッドがロックを獲得しているときにだけ、呼出すことができます。

このメソッドは基礎ロックを解放し、他のスレッドで同じ条件変数の notify()またはnotifyAll()呼出しによって起こされるまで、 あるいはタイムアウトするまでブロックします。 いったん起こされたりタイムアウトしたなら、ロックを再獲得して戻ります。

timeout引数がNone以外で与えられたとき、 それはタイムアウトを秒(またはその分数)で指定する浮動小数であるべきです。

基礎ロックがRLockの場合、それが複数回再帰的に獲得さられたときに アンロックされないのでrelease()メソッドを使っては解放されません。 代わりに、RLockクラスの内部インターフェースが使うことで、実際には 再帰的に複数回獲得されたときにアンロックします。 そして他の内部インターフェースはロックが再獲得されたときに、 再帰レベルを元に戻すことで使用されます。

notify( )
もし存在するなら、この条件を待っているスレッドを起こします。 これは呼出しスレッドがロックを獲得しているときにだけ、呼出すように しなければいけません。

もし待っているスレッドがあるのなら、このメソッドは条件変数を 待っているスレッドのうちの1つを起こします。 もし待っているスレッドがないのなら、なにもしません。

もし待っているスレッドがあるのなら、現在の実装ではまさしく1つの スレッドを起こします。 しかしこの振舞いに依存するのは安全ではありません。 将来、最適化された実装は1つ以上のスレッドを起こすかもしれません。

Note: 起こされたスレッドは実際にロックを再獲得できるまでwait() 呼出しから戻りません。 notify()はロックを解放しないからです。

notifyAll( )
この条件を待っているすべてのスレッドを起こします。 このメソッドはnotify()のように作用しますが、 1つではなくすべての待ちスレッドを起こします。

ご意見やご指摘をお寄せになりたい方は、 このドキュメントについて... をご覧ください。