18.5.3. タスクとコルーチン—Pythonドキュメント

提供:Dev Guides
< PythonPython/docs/3.6/library/asyncio-task
移動先:案内検索

18.5.3。 タスクとコルーチン

ソースコード: :source: `Lib / asyncio / tasks.py`

ソースコード: :source: `Lib / asyncio / coroutines.py`

18.5.3.1。 コルーチン

asyncio で使用されるコルーチンは、 async def ステートメントを使用するか、ジェネレーターを使用して実装できます。 async def タイプのコルーチンがPython3.5で追加されたため、古いバージョンのPythonをサポートする必要がない場合に推奨されます。

ジェネレータベースのコルーチンは、 @ asyncio.coroutine で装飾する必要がありますが、これは厳密には強制されません。 デコレータは、 async def コルーチンとの互換性を可能にし、ドキュメントとしても機能します。 ジェネレータベースのコルーチンは、元のyield構文の代わりに、 PEP 380 で導入されたyield from構文を使用します。

「コルーチン」という単語は、「ジェネレーター」という単語と同様に、2つの異なる(関連はあるものの)概念に使用されます。

  • コルーチンを定義する関数( async def を使用するか、@asyncio.coroutineで装飾された関数定義)。 曖昧性解消が必要な場合は、これをコルーチン関数と呼びます( iscoroutinefunction()Trueを返します)。
  • コルーチン関数を呼び出して取得したオブジェクト。 このオブジェクトは、最終的に完了する計算またはI / O操作(通常は組み合わせ)を表します。 曖昧性解消が必要な場合は、コルーチンオブジェクトと呼びます( iscoroutine()Trueを返します)。

コルーチンでできること:

  • result = await futureまたはresult = yield from future –フューチャーが完了するまでコルーチンを一時停止してから、フューチャーの結果を返すか、例外を発生させて伝播します。 (先物がキャンセルされると、CancelledError例外が発生します。)タスクは先物であり、先物について述べられていることはすべてタスクにも当てはまることに注意してください。
  • result = await coroutineまたはresult = yield from coroutine –別のコルーチンが結果を生成するのを待ちます(または例外を発生させます。これは伝播されます)。 coroutine式は、別のコルーチンへの呼び出しである必要があります。
  • return expressionawait またはyield fromを使用して、これを待機しているコルーチンに結果を生成します。
  • raise exceptionawait またはyield fromを使用して、これを待機しているコルーチンで例外を発生させます。

コルーチンを呼び出しても、コードの実行は開始されません。呼び出しによって返されるコルーチンオブジェクトは、実行をスケジュールするまで何も実行しません。 実行を開始するには、2つの基本的な方法があります。別のコルーチンからawait coroutineまたはyield from coroutineを呼び出すか(他のコルーチンがすでに実行されていると仮定)、 ensure_future()を使用して実行をスケジュールします。 関数または AbstractEventLoop.create_task()メソッド。

コルーチン(およびタスク)は、イベントループが実行されている場合にのみ実行できます。

@asyncio.coroutine

ジェネレータベースのコルーチンをマークするデコレータ。 これにより、ジェネレーターはyield fromを使用して async def コルーチンを呼び出すことができます。また、ジェネレーターを async def コルーチンから呼び出すこともできます。たとえば、 awaitを使用します。 式。

async def コルーチン自体を装飾する必要はありません。

ジェネレーターが破棄される前から生成されていない場合は、エラーメッセージがログに記録されます。 スケジュールされていないコルーチンの検出を参照してください。

ノート

このドキュメントでは、 Future を返すプレーンなPython関数であっても、一部のメソッドはコルーチンとしてドキュメント化されています。 これは、将来これらの機能の実装を自由に調整できるようにすることを目的としています。 このような関数をコールバックスタイルのコードで使用する必要がある場合は、その結果を ensure_future()でラップします。


18.5.3.1.1。 例:HelloWorldコルーチン

"Hello World"を表示するコルーチンの例:

import asyncio

async def hello_world():
    print("Hello World!")

loop = asyncio.get_event_loop()
# Blocking call which returns when the hello_world() coroutine is done
loop.run_until_complete(hello_world())
loop.close()

も参照してください

Hello World with call_soon()の例では、 AbstractEventLoop.call_soon()メソッドを使用してコールバックをスケジュールします。


18.5.3.1.2。 例:現在の日付を表示するコルーチン

sleep()関数を使用して、現在の日付を5秒間毎秒表示するコルーチンの例:

import asyncio
import datetime

async def display_date(loop):
    end_time = loop.time() + 5.0
    while True:
        print(datetime.datetime.now())
        if (loop.time() + 1.0) >= end_time:
            break
        await asyncio.sleep(1)

loop = asyncio.get_event_loop()
# Blocking call which returns when the display_date() coroutine is done
loop.run_until_complete(display_date(loop))
loop.close()

も参照してください

call_later()で現在の日付を表示するの例では、 AbstractEventLoop.call_later()メソッドでコールバックを使用します。


18.5.3.1.3。 例:チェーンコルーチン

コルーチンの連鎖の例:

import asyncio

async def compute(x, y):
    print("Compute %s + %s ..." % (x, y))
    await asyncio.sleep(1.0)
    return x + y

async def print_sum(x, y):
    result = await compute(x, y)
    print("%s + %s = %s" % (x, y, result))

loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()

compute()print_sum()にチェーンされます:print_sum()コルーチンは、compute()が完了するまで待機してから結果を返します。

例のシーケンス図:

class=align-center|../_images/tulip_coro.png 「タスク」は、タスクではなくコルーチンオブジェクトを取得するときに、 AbstractEventLoop.run_until_complete()メソッドによって作成されます。

この図は制御フローを示していますが、内部でどのように機能するかを正確に説明しているわけではありません。 たとえば、スリープコルーチンは、 AbstractEventLoop.call_later()を使用して1秒でタスクをウェイクアップする内部フューチャーを作成します。


18.5.3.2。 InvalidStateError

exception asyncio.InvalidStateError
この状態では操作できません。


18.5.3.3。 TimeoutError

exception asyncio.TimeoutError
操作が指定された期限を超えました。

ノート

この例外は、組み込みの TimeoutError 例外とは異なります。


18.5.3.4。 未来

class asyncio.Future(\*, loop=None)

このクラスはほぼconcurrent.futures.Future と互換性があります。

違い:

このクラスはスレッドセーフではありません

cancel()

将来をキャンセルし、コールバックをスケジュールします。

将来がすでに完了またはキャンセルされている場合は、Falseを返します。 それ以外の場合は、futureの状態をキャンセルに変更し、コールバックをスケジュールして、Trueを返します。

cancelled()

将来がキャンセルされた場合は、Trueを返します。

done()

将来が完了した場合は、Trueを返します。

完了とは、結果/例外が利用可能であるか、将来がキャンセルされたことを意味します。

result()

この未来が表す結果を返します。

未来がキャンセルされた場合、CancelledErrorを上げます。 将来の結果がまだ利用できない場合は、 InvalidStateError を発生させます。 futureが実行され、例外が設定されている場合、この例外が発生します。

exception()

この未来に設定された例外を返します。

例外(または例外が設定されていない場合はNone)は、futureが実行された場合にのみ返されます。 未来がキャンセルされた場合、CancelledErrorを上げます。 将来がまだ完了していない場合は、 InvalidStateError を発生させます。

add_done_callback(fn)

将来が完了したときに実行されるコールバックを追加します。

コールバックは、単一の引数(futureオブジェクト)で呼び出されます。 これが呼び出されたときにfutureがすでに実行されている場合、コールバックは call_soon()でスケジュールされます。

functools.partialを使用して、コールバックにパラメーターを渡します。 たとえば、fut.add_done_callback(functools.partial(print, "Future:", flush=True))print("Future:", fut, flush=True)を呼び出します。

remove_done_callback(fn)

「完了時の呼び出し」リストからコールバックのすべてのインスタンスを削除します。

削除されたコールバックの数を返します。

set_result(result)

完了した未来をマークし、その結果を設定します。

このメソッドが呼び出されたときにfutureがすでに実行されている場合は、 InvalidStateError が発生します。

set_exception(exception)

完了をマークし、例外を設定します。

このメソッドが呼び出されたときにfutureがすでに実行されている場合は、 InvalidStateError が発生します。

18.5.3.4.1。 例:run_until_complete()を使用したFuture

Futureコルーチン関数を組み合わせた例:

import asyncio

async def slow_operation(future):
    await asyncio.sleep(1)
    future.set_result('Future is done!')

loop = asyncio.get_event_loop()
future = asyncio.Future()
asyncio.ensure_future(slow_operation(future))
loop.run_until_complete(future)
print(future.result())
loop.close()

コルーチン関数は計算(1秒かかります)を担当し、結果を将来に保存します。 run_until_complete()メソッドは、futureの完了を待ちます。

ノート

run_until_complete()メソッドは、内部的に add_done_callback()メソッドを使用して、将来が完了したときに通知を受けます。


18.5.3.4.2。 例:run_forever()を使用したFuture

前の例は、 Future.add_done_callback()メソッドを使用して別の方法で記述し、制御フローを明示的に記述することができます。

import asyncio

async def slow_operation(future):
    await asyncio.sleep(1)
    future.set_result('Future is done!')

def got_result(future):
    print(future.result())
    loop.stop()

loop = asyncio.get_event_loop()
future = asyncio.Future()
asyncio.ensure_future(slow_operation(future))
future.add_done_callback(got_result)
try:
    loop.run_forever()
finally:
    loop.close()

この例では、futureを使用してslow_operation()got_result()にリンクしています。slow_operation()が完了すると、got_result()が呼び出されて結果が返されます。


18.5.3.5。 タスク

class asyncio.Task(coro, \*, loop=None)

コルーチンの実行をスケジュールします。将来的にラップします。 タスクは Future のサブクラスです。

タスクは、イベントループでコルーチンオブジェクトを実行する責任があります。 ラップされたコルーチンがfutureから派生した場合、タスクはラップされたコルーチンの実行を一時停止し、futureの完了を待ちます。 futureが完了すると、ラップされたコルーチンの実行が再開され、futureの結果または例外が発生します。

イベントループは協調スケジューリングを使用します。イベントループは一度に1つのタスクのみを実行します。 他のイベントループが異なるスレッドで実行されている場合、他のタスクが並行して実行される可能性があります。 タスクがfutureの完了を待機している間、イベントループは新しいタスクを実行します。

タスクのキャンセルは、将来のキャンセルとは異なります。 cancel()を呼び出すと、ラップされたコルーチンに CancelledError がスローされます。 cancelled()は、ラップされたコルーチンが CancelledError 例外をキャッチしなかった場合、または CancelledError 例外を発生させた場合にのみ、Trueを返します。

保留中のタスクが破棄された場合、ラップされたコルーチンの実行は完了しませんでした。 これはおそらくバグであり、警告がログに記録されます。保留中のタスクが破棄されましたを参照してください。

Task インスタンスを直接作成しないでください。 ensure_future()関数または AbstractEventLoop.create_task()メソッドを使用してください。

このクラスはスレッドセーフではありません

classmethod all_tasks(loop=None)

イベントループのすべてのタスクのセットを返します。

デフォルトでは、現在のイベントループのすべてのタスクが返されます。

classmethod current_task(loop=None)

現在実行中のタスクをイベントループまたはNoneで返します。

デフォルトでは、現在のイベントループの現在のタスクが返されます。

Noneは、タスクのコンテキストではなく呼び出されたときに返されます。

cancel()

このタスクが自動的にキャンセルされるように要求します。

これにより、 CancelledError が、イベントループの次のサイクルでラップされたコルーチンにスローされるようになります。 その後、コルーチンは、try / exception / finallyを使用してリクエストをクリーンアップまたは拒否する機会があります。

Future.cancel()とは異なり、これはタスクがキャンセルされることを保証するものではありません。例外がキャッチされて処理され、タスクのキャンセルが遅れたり、キャンセルが完全に妨げられたりする可能性があります。 タスクは、値を返したり、別の例外を発生させたりする場合もあります。

このメソッドが呼び出された直後、 cancelled()Trueを返しません(タスクがすでにキャンセルされていない限り)。 ラップされたコルーチンが CancelledError 例外で終了すると、タスクはキャンセル済みとしてマークされます( cancel()が呼び出されなかった場合でも)。

get_stack(\*, limit=None)

このタスクのコルーチンのスタックフレームのリストを返します。

コルーチンが実行されていない場合、これは中断されているスタックを返します。 コルーチンが正常に完了したかキャンセルされた場合、これは空のリストを返します。 コルーチンが例外によって終了した場合、これはトレースバックフレームのリストを返します。

フレームは常に古いものから新しいものの順に並べられます。

オプションの制限は、返すフレームの最大数を示します。 デフォルトでは、使用可能なすべてのフレームが返されます。 その意味は、スタックとトレースバックのどちらが返されるかによって異なります。スタックの最新のフレームが返されますが、トレースバックの最も古いフレームが返されます。 (これは、トレースバックモジュールの動作と一致します。)

制御できない理由により、中断されたコルーチンに対して1つのスタックフレームのみが返されます。

print_stack(\*, limit=None, file=None)

このタスクのコルーチンのスタックまたはトレースバックを印刷します。

これにより、get_stack()によって取得されたフレームに対して、トレースバックモジュールと同様の出力が生成されます。 limit引数はget_stack()に渡されます。 file引数は、出力が書き込まれるI / Oストリームです。 デフォルトでは、出力はsys.stderrに書き込まれます。

18.5.3.5.1。 例:タスクの並列実行

3つのタスク(A、B、C)を並行して実行する例:

import asyncio

async def factorial(name, number):
    f = 1
    for i in range(2, number+1):
        print("Task %s: Compute factorial(%s)..." % (name, i))
        await asyncio.sleep(1)
        f *= i
    print("Task %s: factorial(%s) = %s" % (name, number, f))

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(
    factorial("A", 2),
    factorial("B", 3),
    factorial("C", 4),
))
loop.close()

出力:

Task A: Compute factorial(2)...
Task B: Compute factorial(2)...
Task C: Compute factorial(2)...
Task A: factorial(2) = 2
Task B: Compute factorial(3)...
Task C: Compute factorial(3)...
Task B: factorial(3) = 6
Task C: Compute factorial(4)...
Task C: factorial(4) = 24

タスクは、作成時に自動的に実行がスケジュールされます。 すべてのタスクが完了すると、イベントループが停止します。


18.5.3.6。 タスク機能

ノート

以下の関数では、オプションの loop 引数を使用して、基になるタスクまたはコルーチンで使用されるイベントループオブジェクトを明示的に設定できます。 指定されていない場合は、デフォルトのイベントループが使用されます。


asyncio.as_completed(fs, \*, loop=None, timeout=None)

待機時に値が Future インスタンスであるイテレータを返します。

すべての先物が完了する前にタイムアウトが発生した場合、 asyncio.TimeoutError を発生させます。

例:

for f in as_completed(fs):
    result = yield from f  # The 'yield from' may raise
    # Use result

ノート

先物fは必ずしもfsのメンバーではありません。

asyncio.ensure_future(coro_or_future, \*, loop=None)

コルーチンオブジェクトの実行をスケジュールします。将来的にラップします。 Task オブジェクトを返します。

引数が Future の場合、直接返されます。

バージョン3.4.4の新機能。

バージョン3.5.1で変更:この関数は、待機可能なオブジェクトを受け入れます。

も参照してください

AbstractEventLoop.create_task()メソッド。

asyncio.async(coro_or_future, \*, loop=None)

ensure_future()の非推奨のエイリアス。

バージョン3.4.4以降非推奨。

asyncio.wrap_future(future, \*, loop=None)
concurrent.futures.Future オブジェクトを Future オブジェクトでラップします。
asyncio.gather(\*coros_or_futures, loop=None, return_exceptions=False)

指定されたコルーチンオブジェクトまたは先物からの先物集計結果を返します。

すべての先物は同じイベントループを共有する必要があります。 すべてのタスクが正常に実行された場合、返されるfutureの結果は結果のリストです(元のシーケンスの順序であり、必ずしも結果の到着の順序である必要はありません)。 return_exceptions がtrueの場合、タスクの例外は成功した結果と同じように扱われ、結果リストに収集されます。 それ以外の場合、最初に発生した例外は、返されたfutureにすぐに伝播されます。

キャンセル:外側のFutureがキャンセルされると、(まだ完了していない)すべての子もキャンセルされます。 いずれかの子がキャンセルされた場合、これは CancelledError を発生させたかのように扱われます。この場合、外側のFutureはキャンセルされません。 (これは、1人の子がキャンセルされて他の子がキャンセルされるのを防ぐためです。)

バージョン3.6.6で変更: collect 自体がキャンセルされた場合、 return_exceptions に関係なくキャンセルが伝播されます。

asyncio.iscoroutine(obj)
objコルーチンオブジェクトの場合はTrueを返します。これは、ジェネレーターまたは async def コルーチンに基づいている可能性があります。
asyncio.iscoroutinefunction(func)
funcコルーチン関数であると判断された場合はTrueを返します。これは、装飾されたジェネレーター関数または async def 関数である可能性があります。
asyncio.run_coroutine_threadsafe(coro, loop)

コルーチンオブジェクトを特定のイベントループに送信します。

結果にアクセスするには、 concurrent.futures.Future を返します。

この関数は、イベントループが実行されているスレッドとは異なるスレッドから呼び出されることを意図しています。 使用法:

# 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

コルーチンで例外が発生した場合、返される未来が通知されます。 また、イベントループでタスクをキャンセルするために使用することもできます。

try:
    result = future.result(timeout)
except asyncio.TimeoutError:
    print('The coroutine took too long, cancelling the task...')
    future.cancel()
except Exception as exc:
    print('The coroutine raised an exception: {!r}'.format(exc))
else:
    print('The coroutine returned: {!r}'.format(result))

ドキュメントの同時実行およびマルチスレッドセクションを参照してください。

ノート

モジュールの他の関数とは異なり、 run_coroutine_threadsafe()では、 loop 引数を明示的に渡す必要があります。

バージョン3.5.1の新機能。