asyncio.ensure_future と BaseEventLoop.create_task とシンプルなコルーチンの違いは? 質問する

asyncio.ensure_future と BaseEventLoop.create_task とシンプルなコルーチンの違いは? 質問する

私は、さまざまな方法で同じ操作を実行する asyncio に関する基本的な Python 3.5 チュートリアルをいくつか見てきました。このコードでは:

import asyncio  

async def doit(i):
    print("Start %d" % i)
    await asyncio.sleep(3)
    print("End %d" % i)
    return i

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    #futures = [asyncio.ensure_future(doit(i), loop=loop) for i in range(10)]
    #futures = [loop.create_task(doit(i)) for i in range(10)]
    futures = [doit(i) for i in range(10)]
    result = loop.run_until_complete(asyncio.gather(*futures))
    print(result)

変数を定義する上記の 3 つのバリアントはすべてfutures同じ結果を達成します。私が確認した唯一の違いは、3 番目のバリアントでは実行が順序どおりに行われないことです (ほとんどの場合、これは問題になりません)。他に違いはありますか? 最も単純なバリアント (コルーチンの単純なリスト) を使用できないケースはありますか?

ベストアンサー1

実際の情報:

asyncio.create_task(coro)Python 3.7の高レベル関数から始める追加されたこの目的のために。

coroutimes からタスクを作成する他の方法の代わりに、これを使用する必要があります。ただし、任意の awaitable からタスクを作成する必要がある場合は、を使用する必要がありますasyncio.ensure_future(obj)


古い情報:

ensure_futurecreate_task

ensure_future作成する方法ですTaskからcoroutine引数に基づいてさまざまな方法でタスクを作成します (コルーチンや future のようなオブジェクトに を使用するなどcreate_task)。

create_taskは の抽象メソッドですAbstractEventLoop。イベント ループによってこの関数の実装方法は異なります。

タスクを作成するには を使用する必要があります。独自のイベント ループ タイプを実装する場合にのみensure_future必要になります。create_task

更新:

@bj0が指摘したグイドの答えこのトピックにおいて:

のポイントensure_future()は、コルーチンまたは(のサブクラスであるためFuture、後者には が含まれます)のいずれかになるものがあり、 でのみ定義されているメソッド(おそらく が唯一の有用な例です)を呼び出せるようにしたい場合です。 がすでに(または)である場合、これは何もしません。コルーチンである場合は、TaskFutureFuturecancel()FutureTaskラップそれをTask

コルーチンがあることがわかっていて、それをスケジュールしたい場合は、使用する正しい API は ですcreate_task()。 を呼び出す必要があるのは、ensure_future()コルーチンまたは のいずれかを受け入れる API (ほとんどの asyncio 独自の API など) を提供しFuture、 を必要とする操作をその API に対して実行する必要がある場合のみですFuture

以降:

結局のところ、ensure_future()めったに必要とされない機能に対しては、 は適切なわかりにくい名前だと思っています。コルーチンからタスクを作成するときは、適切な名前の を使用する必要がありますloop.create_task()。おそらく、それには別名が必要でしょうかasyncio.create_task()?

私にとっては驚きです。私がensure_futureずっとこれを使う主な動機は、ループのメンバーと比較して、これがより高レベルの関数であるということでしたcreate_task(議論含むasyncio.spawnまたは を追加するなどのアイデアもありますasyncio.create_task

Awaitableまた、私の意見では、コルーチンのみではなく、あらゆるものを処理できるユニバーサル関数を使用するのが非常に便利だとも言えます。

しかし、グイドの答えは明確です。「コルーチンからタスクを作成するときは、適切な名前のloop.create_task()

コルーチンをタスクにラップする必要があるのはいつですか?

コルーチンをタスクにラップする - これは、このコルーチンを「バックグラウンド」で開始する方法です。次に例を示します。

import asyncio


async def msg(text):
    await asyncio.sleep(0.1)
    print(text)


async def long_operation():
    print('long_operation started')
    await asyncio.sleep(3)
    print('long_operation finished')


async def main():
    await msg('first')

    # Now you want to start long_operation, but you don't want to wait it finised:
    # long_operation should be started, but second msg should be printed immediately.
    # Create task to do so:
    task = asyncio.ensure_future(long_operation())

    await msg('second')

    # Now, when you want, you can await task finised:
    await task


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

出力:

first
long_operation started
second
long_operation finished

違いを実感するためにasyncio.ensure_future(long_operation())、 と交換することもできます。await long_operation()

おすすめ記事