同期プリミティブ¶
ソースコード: Lib/asyncio/locks.py
asyncio の同期プリミティブは threading モジュールのそれと類似するようにデザインされていますが、2つの重要な注意事項があります:
asyncio の同期プリミティブはスレッドセーフではありません。従って OS スレッドの同期に使うべきではありません (代わりに
threadingを使ってください);同期プリミティブのメソッドは timeout 引数を受け付けません; タイムアウトを伴う操作を実行するには
asyncio.wait_for()関数を使ってください。
asyncio モジュールは以下の基本的な同期プリミティブを持っています:
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()¶
イベントをセットします。
イベントがセットされるまで待機している全てのタスクは、即座に通知を受けて実行を再開します。
- is_set()¶
イベントがセットされている場合
Trueを返します。
Condition¶
- class asyncio.Condition(lock=None)¶
条件変数オブジェクトです。スレッドセーフではありません。
asyncio 条件プリミティブは何らかのイベントが発生するのを待ち受け、そのイベントを契機として共有リソースへの排他的なアクセスを得るために利用することができます。
本質的に、 Condition オブジェクトは
Eventと aLockの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()を使う方が適している場合もあります。
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 を使ってください。