Python 3.5 の coroutine と future/task の違いは何ですか? 質問する

Python 3.5 の coroutine と future/task の違いは何ですか? 質問する

ダミー関数があるとします:

async def foo(arg):
    result = await some_remote_call(arg)
    return result.upper()

次の違いは何ですか?

import asyncio    

coros = []
for i in range(5):
    coros.append(foo(i))

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(coros))

そして:

import asyncio

futures = []
for i in range(5):
    futures.append(asyncio.ensure_future(foo(i)))

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(futures))

注記: この例では結果が返されますが、これは質問の焦点では​​ありません。戻り値が重要な場合は、gather()の代わりにを使用しますwait()

戻り値に関係なく、 の明確化を求めていますensure_future()wait(coros)と はwait(futures)両方ともコルーチンを実行するので、いつ、なぜコルーチンを でラップする必要があるのでしょうかensure_future?

基本的に、Python 3.5 を使用して一連の非ブロッキング操作を実行する正しい方法 (tm) は何でしょうかasync?

追加の質問ですが、呼び出しをバッチ処理したい場合はどうすればよいでしょうか。たとえば、some_remote_call(...)1000 回呼び出す必要がありますが、1000 の同時接続で Web サーバーやデータベースなどをクラッシュさせたくはありません。これはスレッドまたはプロセス プールで実行できますが、 でこれを行う方法はありますかasyncio

2020 アップデート (Python 3.7+): これらのスニペットは使用しないでください。代わりに以下を使用してください:

import asyncio

async def do_something_async():
    tasks = []
    for i in range(5):
        tasks.append(asyncio.create_task(foo(i)))
    await asyncio.gather(*tasks)

def do_something():
    asyncio.run(do_something_async)

使用も検討してくださいトリオ、asyncio の強力なサードパーティの代替品。

ベストアンサー1

コルーチンは、値を生成したり、外部から値を受け入れたりできるジェネレーター関数です。コルーチンを使用する利点は、関数の実行を一時停止して後で再開できることです。ネットワーク操作の場合、応答を待っている間、関数の実行を一時停止するのは理にかなっています。その時間を利用して、他の関数を実行できます。

future は、PromiseJavascript のオブジェクトのようなものです。これは、将来実現される値のプレースホルダーのようなものです。前述のケースでは、ネットワーク I/O を待機している間、関数はコンテナーを提供できます。これは、操作が完了したときにコンテナーに値を入れるという約束です。future オブジェクトを保持し、それが満たされると、そのオブジェクト上のメソッドを呼び出して実際の結果を取得できます。

直接的な回答:ensure_future結果が必要ない場合は必要ありません。結果が必要な場合や、発生した例外を取得する場合に適しています。

追加クレジット:私は選ぶrun_in_executorExecutor最大ワーカー数を制御するインスタンスを渡します。

説明とサンプルコード

最初の例では、コルーチンを使用しています。このwait関数は、一連のコルーチンを受け取り、それらを組み合わせます。したがって、wait()すべてのコルーチンが使い果たされると終了します (すべての値を返して完了/終了)。

loop = get_event_loop() # 
loop.run_until_complete(wait(coros))

このrun_until_completeメソッドは、実行が完了するまでループが継続していることを確認します。この場合、非同期実行の結果が取得されないことに注意してください。

2 番目の例では、ensure_future関数を使用してコルーチンをラップし、Taskの一種であるオブジェクトを返しますFuture。コルーチンは、 を呼び出すときにメイン イベント ループで実行されるようにスケジュールされますensure_future。返された future/task オブジェクトにはまだ値がありませんが、時間の経過とともにネットワーク操作が終了すると、future オブジェクトに操作の結果が保持されます。

from asyncio import ensure_future

futures = []
for i in range(5):
    futures.append(ensure_future(foo(i)))

loop = get_event_loop()
loop.run_until_complete(wait(futures))

この例では、コルーチンを使用する代わりにフューチャーを使用している点を除いて同じことを行っています。

asyncio/coroutines/futures の使用方法の例を見てみましょう。

import asyncio


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


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

    # We have result, so let's stop
    loop.stop()


loop = asyncio.get_event_loop()
task = loop.create_task(slow_operation())
task.add_done_callback(got_result)

# We run forever
loop.run_forever()

create_taskここでは、オブジェクトの メソッドを使用しましたloopensure_futureメイン イベント ループでタスクをスケジュールします。このメソッドを使用すると、選択したループでコルーチンをスケジュールできます。

add_done_callbackまた、タスク オブジェクトのメソッドを使用してコールバックを追加するという概念も確認します。

Aは、コルーチンが値を返したり、例外を発生させたり、キャンセルされたりしたときTaskです。これらのインシデントをチェックするメソッドがあります。done

以下のトピックについて役立つかもしれないブログ記事をいくつか書きました:

もちろん、公式マニュアルでさらに詳しい情報を確認できます。python.io のライブラリ

おすすめ記事