コルーチンとタスク
このセクションでは、コルーチンとタスクを操作するための高レベルの非同期APIの概要を説明します。
- コルーチン
- 待てます
- asyncioプログラムの実行
- タスクの作成
- 睡眠
- タスクを同時に実行する
- キャンセルからの保護
- タイムアウト
- 待機中のプリミティブ
- スレッドで実行
- 他のスレッドからのスケジューリング
- 内省
- タスクオブジェクト
- ジェネレータベースのコルーチン
コルーチン
async / await構文で宣言された Coroutines は、asyncioアプリケーションを作成するための推奨される方法です。 たとえば、次のコードスニペット(Python 3.7以降が必要)は「hello」を出力し、1秒間待機してから、「world」を出力します。
>>> import asyncio
>>> async def main():
... print('hello')
... await asyncio.sleep(1)
... print('world')
>>> asyncio.run(main())
hello
world
コルーチンを呼び出すだけでは、実行がスケジュールされないことに注意してください。
>>> main()
<coroutine object main at 0x1053bb7c8>
コルーチンを実際に実行するために、asyncioは3つの主要なメカニズムを提供します。
asyncio.run()関数は、最上位のエントリポイント「main()」関数を実行します(上記の例を参照)。
コルーチンで待っています。 次のコードスニペットは、1秒待った後に「hello」を出力し、 another 2秒待った後に「world」を出力します。
import asyncio import time async def say_after(delay, what): await asyncio.sleep(delay) print(what) async def main(): print(f"started at {time.strftime('%X')}") await say_after(1, 'hello') await say_after(2, 'world') print(f"finished at {time.strftime('%X')}") asyncio.run(main())
期待される出力:
started at 17:13:52 hello world finished at 17:13:55
asyncio.create_task()関数は、コルーチンをasyncio Tasks として同時に実行します。
上記の例を変更して、2つの
say_after
コルーチンを同時に実行してみましょう。async def main(): task1 = asyncio.create_task( say_after(1, 'hello')) task2 = asyncio.create_task( say_after(2, 'world')) print(f"started at {time.strftime('%X')}") # Wait until both tasks are completed (should take # around 2 seconds.) await task1 await task2 print(f"finished at {time.strftime('%X')}")
期待される出力は、スニペットが以前より1秒速く実行されることを示していることに注意してください。
started at 17:14:32 hello world finished at 17:14:34
待てます
オブジェクトが await 式で使用できる場合、そのオブジェクトは awaitable オブジェクトであると言います。 多くのasyncioAPIは、待機可能ファイルを受け入れるように設計されています。
awaitable オブジェクトには、コルーチン、タスク、フューチャーの3つの主要なタイプがあります。
コルーチン
Pythonコルーチンは待機可能であるため、他のコルーチンから待機できます。
import asyncio
async def nested():
return 42
async def main():
# Nothing happens if we just call "nested()".
# A coroutine object is created but not awaited,
# so it *won't run at all*.
nested()
# Let's do it differently now and await it:
print(await nested()) # will print "42".
asyncio.run(main())
重要
このドキュメントでは、「コルーチン」という用語は、密接に関連する2つの概念に使用できます。
- コルーチン関数: async def 関数。
- コルーチンオブジェクト:コルーチン関数を呼び出すことによって返されるオブジェクト。
asyncioは、レガシージェネレーターベースコルーチンもサポートします。
タスク
タスクは、コルーチンを同時にスケジュールするために使用されます。
コルーチンが asyncio.create_task()のような関数で Task にラップされると、コルーチンはすぐに実行されるように自動的にスケジュールされます。
import asyncio
async def nested():
return 42
async def main():
# Schedule nested() to run soon concurrently
# with "main()".
task = asyncio.create_task(nested())
# "task" can now be used to cancel "nested()", or
# can simply be awaited to wait until it is complete:
await task
asyncio.run(main())
先物
Future は、非同期操作の最終結果を表す特別な低レベル待機可能オブジェクトです。
Futureオブジェクトが待機中の場合、コルーチンは、Futureが他の場所で解決されるまで待機することを意味します。
コールバックベースのコードをasync / awaitで使用できるようにするには、asyncioの将来のオブジェクトが必要です。
通常、アプリケーションレベルのコードでFutureオブジェクトを作成する必要はありません。
ライブラリや一部のasyncioAPIによって公開されることがある、将来のオブジェクトを待つことができます。
async def main():
await function_that_returns_a_future_object()
# this is also valid:
await asyncio.gather(
function_that_returns_a_future_object(),
some_python_coroutine()
)
Futureオブジェクトを返す低レベル関数の良い例は、loop.run_in_executor()
です。
asyncioプログラムの実行
- asyncio.run(coro, *, debug=False)
コルーチン コロを実行して結果を返します。
この関数は、渡されたコルーチンを実行し、asyncioイベントループの管理、非同期ジェネレーターのファイナライズ、およびスレッドプールのクローズを行います。
同じスレッドで別のasyncioイベントループが実行されている場合、この関数を呼び出すことはできません。
debug が
True
の場合、イベントループはデバッグモードで実行されます。この関数は常に新しいイベントループを作成し、最後にそれを閉じます。 これは、asyncioプログラムのメインエントリポイントとして使用する必要があり、理想的には1回だけ呼び出す必要があります。
例:
async def main(): await asyncio.sleep(1) print('hello') asyncio.run(main())
バージョン3.7の新機能。
バージョン3.9で変更:
loop.shutdown_default_executor()
を使用するように更新。ノート
asyncio.run()
のソースコードは、:source: `Lib / asyncio / runners.py` にあります。
タスクの作成
- asyncio.create_task(coro, *, name=None)
coro coroutine を Task にラップし、その実行をスケジュールします。 Taskオブジェクトを返します。
name が
None
でない場合は、 Task.set_name()を使用してタスクの名前として設定されます。タスクは get_running_loop()によって返されるループで実行され、現在のスレッドに実行中のループがない場合は RuntimeError が発生します。
この関数は、Python 3.7 で追加されました。 Python 3.7より前は、代わりに低レベルの asyncio.ensure_future()関数を使用できました。
async def coro(): ... # In Python 3.7+ task = asyncio.create_task(coro()) ... # This works in all Python versions but is less readable task = asyncio.ensure_future(coro()) ...
バージョン3.7の新機能。
バージョン3.8で変更:
name
パラメーターが追加されました。
睡眠
タスクを同時に実行する
キャンセルからの保護
タイムアウト
待機中のプリミティブ
- asyncio.as_completed(aws, *, loop=None, timeout=None)
同時に反復可能な aws で待機可能オブジェクトを実行します。 コルーチンのイテレータを返します。 返される各コルーチンは、残りの待機可能オブジェクトの反復可能ファイルから最も早い次の結果を取得するために待機できます。
すべての先物が完了する前にタイムアウトが発生した場合、 asyncio.TimeoutError を発生させます。
例:
for coro in as_completed(aws): earliest_result = await coro # ...
スレッドで実行
他のスレッドからのスケジューリング
- asyncio.run_coroutine_threadsafe(coro, loop)
指定されたイベントループにコルーチンを送信します。 スレッドセーフ。
concurrent.futures.Future を返し、別のOSスレッドからの結果を待ちます。
この関数は、イベントループが実行されているものとは異なるOSスレッドから呼び出されることを意図しています。 例:
# Create a coroutine coro = asyncio.sleep(1, result=3) # Submit the coroutine to a given loop future = asyncio.run_coroutine_threadsafe(coro, loop) # Wait for the result with an optional timeout argument assert future.result(timeout) == 3
コルーチンで例外が発生した場合、返されたFutureに通知されます。 また、イベントループでタスクをキャンセルするために使用することもできます。
try: result = future.result(timeout) except asyncio.TimeoutError: print('The coroutine took too long, cancelling the task...') future.cancel() except Exception as exc: print(f'The coroutine raised an exception: {exc!r}') else: print(f'The coroutine returned: {result!r}')
ドキュメントの同時実行およびマルチスレッドセクションを参照してください。
他の非同期関数とは異なり、この関数では loop 引数を明示的に渡す必要があります。
バージョン3.5.1の新機能。
内省
- asyncio.current_task(loop=None)
現在実行中の Task インスタンスを返します。タスクが実行されていない場合は、
None
を返します。loop が
None
の場合、 get_running_loop()を使用して現在のループを取得します。バージョン3.7の新機能。
- asyncio.all_tasks(loop=None)
ループによって実行された、まだ完了していない Task オブジェクトのセットを返します。
loop が
None
の場合、 get_running_loop()が現在のループの取得に使用されます。バージョン3.7の新機能。
タスクオブジェクト
- class asyncio.Task(coro, *, loop=None, name=None)
Python コルーチンを実行する Future-like オブジェクト。 スレッドセーフではありません。
タスクは、イベントループでコルーチンを実行するために使用されます。 コルーチンがFutureを待機している場合、タスクはコルーチンの実行を一時停止し、Futureの完了を待ちます。 Futureが done になると、ラップされたコルーチンの実行が再開されます。
イベントループは協調スケジューリングを使用します。イベントループは一度に1つのタスクを実行します。 タスクがFutureの完了を待機している間、イベントループは他のタスクを実行したり、コールバックしたり、IO操作を実行したりします。
高レベルの asyncio.create_task()関数を使用してタスクを作成するか、低レベルの loop.create_task()または sure_future()関数を使用します。 タスクを手動でインスタンス化することはお勧めしません。
実行中のタスクをキャンセルするには、 cancel()メソッドを使用します。 これを呼び出すと、タスクはラップされたコルーチンに CancelledError 例外をスローします。 キャンセル中にコルーチンがFutureオブジェクトを待機している場合、Futureオブジェクトはキャンセルされます。
cancelled()を使用して、タスクがキャンセルされたかどうかを確認できます。 ラップされたコルーチンが CancelledError 例外を抑制せず、実際にキャンセルされた場合、メソッドは
True
を返します。asyncio.Task は、 Future から、 Future.set_result()と Future.set_exception()を除くすべてのAPIを継承します。
タスクは contextvars モジュールをサポートします。 タスクが作成されると、現在のコンテキストがコピーされ、後でコピーされたコンテキストでコルーチンが実行されます。
バージョン3.7で変更: contextvars モジュールのサポートが追加されました。
バージョン3.8で変更:
name
パラメーターが追加されました。- cancel(msg=None)
タスクのキャンセルをリクエストします。
これにより、 CancelledError 例外が、イベントループの次のサイクルでラップされたコルーチンにスローされるように調整されます。
コルーチンは、 try ……
except CancelledError
… finally ブロックで例外を抑制することにより、要求をクリーンアップまたは拒否する機会があります。 したがって、 Future.cancel()とは異なり、 Task.cancel()は、タスクがキャンセルされることを保証しませんが、キャンセルを完全に抑制することは一般的ではなく、積極的に推奨されていません。バージョン3.9で変更:
msg
パラメーターが追加されました。次の例は、コルーチンがキャンセル要求をインターセプトする方法を示しています。
async def cancel_me(): print('cancel_me(): before sleep') try: # Wait for 1 hour await asyncio.sleep(3600) except asyncio.CancelledError: print('cancel_me(): cancel sleep') raise finally: print('cancel_me(): after sleep') async def main(): # Create a "cancel_me" Task task = asyncio.create_task(cancel_me()) # Wait for 1 second await asyncio.sleep(1) task.cancel() try: await task except asyncio.CancelledError: print("main(): cancel_me is cancelled now") asyncio.run(main()) # Expected output: # # cancel_me(): before sleep # cancel_me(): cancel sleep # cancel_me(): after sleep # main(): cancel_me is cancelled now
- cancelled()
タスクがキャンセルの場合、
True
を返します。cancel()でキャンセルが要求され、ラップされたコルーチンが CancelledError 例外を伝播した場合、タスクはキャンセルされます。
- done()
タスクが完了の場合、
True
を返します。ラップされたコルーチンが値を返すか、例外を発生させるか、タスクがキャンセルされた場合、タスクは完了されます。
- result()
タスクの結果を返します。
タスクが done の場合、ラップされたコルーチンの結果が返されます(または、コルーチンが例外を発生させた場合、その例外が再発生します)。
タスクがキャンセルされている場合、このメソッドは CancelledError 例外を発生させます。
タスクの結果がまだ利用できない場合、このメソッドは InvalidStateError 例外を発生させます。
- exception()
タスクの例外を返します。
ラップされたコルーチンが例外を発生させた場合、その例外が返されます。 ラップされたコルーチンが正常に返される場合、このメソッドは
None
を返します。タスクがキャンセルされている場合、このメソッドは CancelledError 例外を発生させます。
タスクがまだ完了されていない場合、このメソッドは InvalidStateError 例外を発生させます。
- add_done_callback(callback, *, context=None)
タスクが完了のときに実行されるコールバックを追加します。
このメソッドは、低レベルのコールバックベースのコードでのみ使用する必要があります。
詳細については、 Future.add_done_callback()のドキュメントを参照してください。
- remove_done_callback(callback)
コールバックリストから callback を削除します。
このメソッドは、低レベルのコールバックベースのコードでのみ使用する必要があります。
詳細については、 Future.remove_done_callback()のドキュメントを参照してください。
- get_stack(*, limit=None)
このタスクのスタックフレームのリストを返します。
ラップされたコルーチンが実行されない場合、これは中断されているスタックを返します。 コルーチンが正常に完了したかキャンセルされた場合、これは空のリストを返します。 コルーチンが例外によって終了した場合、これはトレースバックフレームのリストを返します。
フレームは常に古いものから新しいものの順に並べられます。
中断されたコルーチンに対して1つのスタックフレームのみが返されます。
オプションの limit 引数は、返すフレームの最大数を設定します。 デフォルトでは、使用可能なすべてのフレームが返されます。 返されるリストの順序は、スタックとトレースバックのどちらが返されるかによって異なります。スタックの最新のフレームが返されますが、トレースバックの最も古いフレームが返されます。 (これは、トレースバックモジュールの動作と一致します。)
- print_stack(*, limit=None, file=None)
このタスクのスタックまたはトレースバックを印刷します。
これにより、 get_stack()によって取得されたフレームのトレースバックモジュールと同様の出力が生成されます。
limit 引数は、 get_stack()に直接渡されます。
file 引数は、出力が書き込まれるI / Oストリームです。 デフォルトでは、出力は sys.stderr に書き込まれます。
- get_coro()
タスクでラップされたコルーチンオブジェクトを返します。
バージョン3.8の新機能。
- get_name()
タスクの名前を返します。
タスクに名前が明示的に割り当てられていない場合、デフォルトのasyncio Task実装は、インスタンス化中にデフォルトの名前を生成します。
バージョン3.8の新機能。
- set_name(value)
タスクの名前を設定します。
value 引数は任意のオブジェクトにすることができ、それが文字列に変換されます。
デフォルトのタスク実装では、名前はタスクオブジェクトの repr()出力に表示されます。
バージョン3.8の新機能。
ジェネレータベースのコルーチン
ノート
ジェネレータベースのコルーチンのサポートは非推奨であり、Python3.10で削除される予定です。
ジェネレーターベースのコルーチンは、async / await構文よりも前のものです。 これらは、yield from
式を使用してFuturesやその他のコルーチンを待機するPythonジェネレーターです。
ジェネレータベースのコルーチンは @ asyncio.coroutine で装飾する必要がありますが、これは強制されません。
- @asyncio.coroutine
ジェネレータベースのコルーチンをマークするデコレータ。
このデコレータを使用すると、レガシージェネレータベースのコルーチンをasync / awaitコードと互換性があります。
@asyncio.coroutine def old_style_coroutine(): yield from asyncio.sleep(1) async def main(): await old_style_coroutine()
このデコレータは、 async def コルーチンには使用しないでください。
- asyncio.iscoroutine(obj)
obj がコルーチンオブジェクトの場合、
True
を返します。このメソッドは、ジェネレーターベースのコルーチンに対して
True
を返すため、 inspect.iscoroutine()とは異なります。
- asyncio.iscoroutinefunction(func)
func がコルーチン関数の場合、
True
を返します。このメソッドは、 @coroutine で装飾されたジェネレータベースのコルーチン関数に対して
True
を返すため、 inspect.iscoroutinefunction()とは異なります。