同期プリミティブ
****************

**ソースコード:** Lib/asyncio/locks.py

======================================================================

asyncio の同期プリミティブは "threading" モジュールのそれと類似するよ
うにデザインされていますが、2つの重要な注意事項があります:

* asyncio の同期プリミティブはスレッドセーフではありません。従って OS
  スレッドの同期に使うべきではありません (代わりに "threading" を使っ
  てください);

* 同期プリミティブのメソッドは *timeout* 引数を受け付けません; タイム
  アウトを伴う操作を実行するには "asyncio.wait_for()" 関数を使ってくだ
  さい。

asyncio モジュールは以下の基本的な同期プリミティブを持っています:

* "Lock"

* "Event"

* "Condition"

* "Semaphore"

* "BoundedSemaphore"

* "Barrier"

======================================================================


Lock
====

class asyncio.Lock

   asyncio タスクのためのミューテックスロックを実装しています。スレッ
   ドセーフではありません。

   asyncio ロックは、共有リソースに対する排他的なアクセスを保証するた
   めに使うことができます。

   Lock の望ましい使用方法は、 "async with" 文と組み合わせて使うことで
   す:

      lock = asyncio.Lock()

      # ... 後で
      async with lock:
          # 共有状態にアクセスする

   これは以下のコードと等価です:

      lock = asyncio.Lock()

      # ... 後で
      await lock.acquire()
      try:
          # 共有状態にアクセスする
      finally:
          lock.release()

   バージョン 3.10 で変更: *loop* パラメータが削除されました。

   async acquire()

      ロックを獲得します。

      このメソッドはロックが *解除される* まで待機し、ロックを *ロック
      状態* に変更して "True" を返します。

      複数のコルーチンが "acquire()" メソッドによりロックの解除を待ち
      受けている場合、最終的にただひとつのコルーチンが実行されます。

      ロックの獲得は *公平* です: すなわちロックを獲得して実行されるコ
      ルーチンは、最初にロックの待ち受けを開始したコルーチンです。

   release()

      ロックを解放します。

      ロックが *ロック状態* の場合、 ロックを *解除状態* にしてリター
      ンします。

      ロックが *解除状態* の場合、 "RuntimeError" 例外が送出されます。

   locked()

      *ロック状態* の場合に "True" を返します。


Event
=====

class asyncio.Event

   イベントオブジェクトです。スレッドセーフではありません。

   asyncio イベントは、複数の asyncio タスクに対して何らかのイベントが
   発生したことを通知するために使うことができます。

   Event オブジェクトは内部フラグを管理します。フラグの値は "set()" メ
   ソッドにより *true* に、また "clear()" メソッドにより *false* に設
   定することができます。 "wait()" メソッドはフラグが *true* になるま
   で処理をブロックします。フラグの初期値は *false* です。

   バージョン 3.10 で変更: *loop* パラメータが削除されました。

   以下はプログラム例です:

      async def waiter(event):
          print('waiting for it ...')
          await event.wait()
          print('... got it!')

      async def main():
          # Event オブジェクトを作成する。
          event = asyncio.Event()

          # 'event' がセットされるまで待機するタスクを起動する。
          waiter_task = asyncio.create_task(waiter(event))

          # 1 秒待ってからイベントをセットする。
          await asyncio.sleep(1)
          event.set()

          # 待機中のタスクが終了するまで待つ。
          await waiter_task

      asyncio.run(main())

   async wait()

      イベントがセットされるまで待機します。

      イベントがセットされると、即座に "True" を返します。 そうでなけ
      れば、他のタスクが "set()" メソッドを呼び出すまで処理をブロック
      します。

   set()

      イベントをセットします。

      イベントがセットされるまで待機している全てのタスクは、即座に通知
      を受けて実行を再開します。

   clear()

      イベントをクリア (アンセット) します

      以降に "wait()" を await するタスクは、"set()" メソッドが再び呼
      び出されるまでブロックされます。

   is_set()

      イベントがセットされている場合 "True" を返します。


Condition
=========

class asyncio.Condition(lock=None)

   条件変数オブジェクトです。スレッドセーフではありません。

   asyncio 条件プリミティブは何らかのイベントが発生するのを待ち受け、
   そのイベントを契機として共有リソースへの排他的なアクセスを得るため
   に利用することができます。

   本質的に、 Condition オブジェクトは "Event" と a "Lock" の2つのクラ
   スの機能を組み合わせたものです。複数の Condition オブジェクトが単一
   の Lock を共有することでができます。これにより、共有リソースのそれ
   ぞれの状態に関連する異なるタスクの間で、そのリソースへの排他的アク
   セスを調整することが可能になります。

   オプション引数 *lock* は "Lock" または "None" でなければなりません
   。後者の場合自動的に新しい Lock オブジェクトが生成されます。

   バージョン 3.10 で変更: *loop* パラメータが削除されました。

   Condition の望ましい使用方法は "async with" 文と組み合わせて使うこ
   とです:

      cond = asyncio.Condition()

      # ... 後で
      async with cond:
          await cond.wait()

   これは以下のコードと等価です:

      cond = asyncio.Condition()

      # ... 後で
      await cond.acquire()
      try:
          await cond.wait()
      finally:
          cond.release()

   async acquire()

      下層でのロックを獲得します。

      このメソッドは下層のロックが  *解除される* まで待機し、ロックを
      *ロック状態* に変更して "True" を返します。

   notify(n=1)

      この条件変数で待機しているタスクを *n* 個 (デフォルトは 1 個) 起
      こします。 待機しているタスクが *n* 個未満なら、すべて起こします
      。

      このメソッドが呼び出される前にロックを獲得しておかなければなりま
      せん。また、メソッド呼び出し後速やかにロックを解除しなければなり
      ません。 *解除された* ロックとと共に呼び出された場合、
      "RuntimeError" 例外が送出されます。

   locked()

      下層のロックを獲得していれば "True" を返します。

   notify_all()

      この条件を待ち受けている全てのタスクを起動します。

      このメソッドは "notify()" と同じように振る舞いますが、待ち受けて
      いる全てのタスクを起動します。

      このメソッドが呼び出される前にロックを獲得しておかなければなりま
      せん。また、メソッド呼び出し後速やかにロックを解除しなければなり
      ません。 *解除された* ロックとと共に呼び出された場合、
      "RuntimeError" 例外が送出されます。

   release()

      下層のロックを解除します。

      アンロック状態のロックに対して呼び出された場合、"RuntimeError"
      が送出されます。

   async wait()

      通知を受けるまで待機します。

      このメソッドが呼び出された時点で呼び出し元のタスクがロックを獲得
      していない場合、 "RuntimeError" 例外が送出されます。

      このメソッドは下層のロックを解除し、その後 "notify()" または
      "notify_all()" の呼び出しによって起動されるまで処理をブロックし
      ます。いったん起動されると、  Condition は再びロックを獲得し、メ
      ソッドは "True" を返します。

      この呼び出しは、通知を受けていなくても返る *場合があります*。 そ
      のため、呼び出し側では必ず状態を再確認し、必要なら再び "wait()"
      できるようにしてください。 このため、代わりに "wait_for()" を使
      う方が適している場合もあります。

   async wait_for(predicate)

      "predicate" の評価結果が *真* になるまで待機します。

      "predicate" は呼び出し可能オブジェクトでなければならず、その結果
      はブール値として解釈されます。 このメソッドは "predicate" の評価
      結果が *真* になるまで "wait()" を繰り返し、最後の評価結果を返し
      ます。


Semaphore
=========

class asyncio.Semaphore(value=1)

   セマフォオブジェクトです。スレッドセーフではありません。

   セマフォは内部のカウンターを管理しています。カウンターは
   "acquire()" メソッドの呼び出しによって減算され、 "release()" メソッ
   ドの呼び出しによって加算されます。カウンターがゼロを下回ることはあ
   りません。 "acquire()" メソッドが呼び出された時にカウンターがゼロに
   なっていると、セマフォは処理をブロックし、他のタスクが "release()"
   メソッドを呼び出すまで待機します。

   オプション引数 *value* は内部カウンターの初期値を与えます (デフォル
   トは "1" です)。 指定された値が "0" より小さい場合、 "ValueError"
   例外が送出されます。

   バージョン 3.10 で変更: *loop* パラメータが削除されました。

   セマフォの望ましい使用方法は、 "async with" 文と組み合わせて使うこ
   とです:

      sem = asyncio.Semaphore(10)

      # ... 後で
      async with sem:
          # 共有リソースを操作する

   これは以下のコードと等価です:

      sem = asyncio.Semaphore(10)

      # ... 後で
      await sem.acquire()
      try:
          # 共有リソースを操作する
      finally:
          sem.release()

   async acquire()

      セマフォを獲得します。

      内部カウンターがゼロより大きい場合、カウンターを1つ減算して即座
      に "True" を返します。内部カウンターがゼロの場合、 "release()"
      が呼び出されるまで待機してから "True" を返します。

   locked()

      セマフォを直ちに獲得できない場合 "True" を返します。

   release()

      セマフォを解放し、内部カウンターを1つ加算します。セマフォ待ちを
      しているタスクを起動する可能性があります。

      "BoundedSemaphore" と異なり、 "Semaphore" は "release()" を
      "acquire()" よりも多く呼び出すことを許容します。


BoundedSemaphore
================

class asyncio.BoundedSemaphore(value=1)

   有限セマフォオブジェクトです。スレッドセーフではありません。

   有限セマフォは "Semaphore" の一種です。 "release()" によって内部カ
   ウンターが初期値 *value* を超える場合、"ValueError" を送出します。

   バージョン 3.10 で変更: *loop* パラメータが削除されました。


Barrier
=======

class asyncio.Barrier(parties)

   バリアオブジェクトです。 スレッドセーフではありません。

   バリアは、*parties* 個のタスクが待機するまでブロックできる単純な同
   期プリミティブです。 タスクは "wait()" メソッドで待機でき、指定した
   数のタスクが "wait()" で待機するまでブロックされます。 その時点で、
   待機しているすべてのタスクのブロックが同時に解除されます。

   "wait()" を await する代わりに "async with" を使えます。

   バリアは何度でも再利用できます。

   以下はプログラム例です:

      async def example_barrier():
         # 3 タスクが参加するバリア
         b = asyncio.Barrier(3)

         # 待機するタスクを 2 つ作成する
         asyncio.create_task(b.wait())
         asyncio.create_task(b.wait())

         await asyncio.sleep(0)
         print(b)

         # 3 回目の .wait() の呼び出しでバリアを通過する
         await b.wait()
         print(b)
         print("barrier passed")

         await asyncio.sleep(0)
         print(b)

      asyncio.run(example_barrier())

   この例の実行結果は次のとおりです。

      <asyncio.locks.Barrier object at 0x... [filling, waiters:2/3]>
      <asyncio.locks.Barrier object at 0x... [draining, waiters:0/3]>
      barrier passed
      <asyncio.locks.Barrier object at 0x... [filling, waiters:0/3]>

   Added in version 3.11.

   async wait()

      バリアを通過します。 バリアに参加するすべてのタスクがこの関数を
      呼び出すと、すべてのタスクのブロックが同時に解除されます。

      バリアで待機中またはブロック中のタスクがキャンセルされると、その
      タスクはバリアから抜けます。 バリアの状態は変わりません。 バリア
      が "filling" 状態の場合、待機中のタスク数は 1 減ります。

      戻り値は 0 から "parties-1" までの整数で、タスクごとに異なります
      。 これを使うと、後片付けなどの特別な処理を行うタスクを選べます
      。 例えば次のようにします。

         ...
         async with barrier as position:
            if position == 0:
               # 1 つのタスクだけがこれを表示する
               print('End of *draining phase*')

      タスクの待機中にバリアが broken 状態になるかリセットされると、
      "BrokenBarrierError" が送出される場合があります。 タスクがキャン
      セルされると "CancelledError" が送出される場合があります。

   async reset()

      バリアをデフォルトの空の状態に戻します。 バリアで待機しているタ
      スクには "BrokenBarrierError" が送出されます。

      バリアが broken 状態になった場合は、そのバリアをそのままにして新
      しく作り直す方がよいこともあります。

   async abort()

      バリアを broken 状態にします。 これにより、実行中および今後の
      "wait()" の呼び出しは "BrokenBarrierError" で失敗します。 例えば
      、いずれかのタスクが処理を中断する必要があるときに、他のタスクが
      無限に待機しないようにするために使います。

   parties

      バリアを通過するために必要なタスク数です。

   n_waiting

      filling 状態のバリアで現在待機しているタスク数です。

   broken

      バリアが broken な状態である場合に "True" となるブール値。

exception asyncio.BrokenBarrierError

   "Barrier" オブジェクトがリセットされるか broken な場合に、この例外
   ("RuntimeError" のサブクラス) が送出されます。

======================================================================

バージョン 3.9 で変更: "await lock" や "yield from lock" およびそれら
と "with" 文との組み合わせ (すなわち "with await lock" や "with (yield
from lock)") によるロックの獲得は削除されました。代わりに "async with
lock" を使ってください。
