# 同期プリミティブ

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

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

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

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

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

- `Lock`

- `Event`

- `Condition`

- `Semaphore`

- `BoundedSemaphore`

## Lock

class `asyncio.``Lock`

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

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

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

      lock = asyncio.Lock()

      # ... later
      async with lock:
          # access shared state

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

      lock = asyncio.Lock()

      # ... later
      await lock.acquire()
      try:
          # access shared state
      finally:
          lock.release()

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

  coroutine `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():
          # Create an Event object.
          event = asyncio.Event()

          # Spawn a Task to wait until 'event' is set.
          waiter_task = asyncio.create_task(waiter(event))

          # Sleep for 1 second and set the event.
          await asyncio.sleep(1)
          event.set()

          # Wait until the waiter task is finished.
          await waiter_task

      asyncio.run(main())

  coroutine `wait`()

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

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

  `set`()

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

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

  `clear`()

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

    `wait()` メソッドで待ち受けを行うタスクは `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()

      # ... later
      async with cond:
          await cond.wait()

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

      cond = asyncio.Condition()

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

  coroutine `acquire`()

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

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

  `notify`(n=1)

    この条件を待ち受けている最大で n 個のタスク (n のデフォルト値は 1) を起動します。待ち受けているタスクがいない場合、このメソッドは何もしません。

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

  `locked`()

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

  `notify_all`()

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

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

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

  `release`()

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

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

  coroutine `wait`()

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

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

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

  coroutine `wait_for`(predicate)

    引数 predicate の条件が 真 になるまで待機します。

    引数 predicate は戻り値が真偽地として解釈可能な呼び出し可能オブジェクトでなければなりません。 predicate の最終的な値が戻り値になります。

## Semaphore

class `asyncio.``Semaphore`(value=1)

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

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

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

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

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

      sem = asyncio.Semaphore(10)

      # ... later
      async with sem:
          # work with shared resource

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

      sem = asyncio.Semaphore(10)

      # ... later
      await sem.acquire()
      try:
          # work with shared resource
      finally:
          sem.release()

  coroutine `acquire`()

    セマフォを獲得します。

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

  `locked`()

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

  `release`()

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

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

## BoundedSemaphore

class `asyncio.``BoundedSemaphore`(value=1)

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

  有限セマフォは `Semaphore` の一種で、 `release()` メソッドの呼び出しにより内部カウンターが 初期値 よりも増加してしまう場合は `ValueError` 例外を送出します。

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

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