web-dev-qa-db-ja.com

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 __== '__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)

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

78
crusaderky

実際の情報:

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

代わりに、同じ時間からタスクを作成する他の方法を使用する必要があります。ただし、任意の待機可能からタスクを作成する必要がある場合は、asyncio.ensure_future(obj)を使用する必要があります。


古い情報:

ensure_future vs create_task

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

create_taskは、AbstractEventLoopの抽象メソッドです。さまざまなイベントループでこの関数をさまざまな方法で実装できます。

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

更新:

@ bj0はこのトピックの Guidoの答え を指しています:

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

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

以降:

結局、ensure_future()は、ほとんど必要のない機能の適切なあいまいな名前だとまだ信じています。コルーチンからタスクを作成するときは、適切な名前のloop.create_task()を使用する必要があります。たぶんそのasyncio.create_task()のエイリアスがあるはずですか?

驚きました。 ensure_futureをずっと使用する主な動機は、ループのメンバーcreate_task(議論 含むasyncio.spawnまたはasyncio.create_task)。

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

しかし、Guidoの答えは明確です: "コルーチンからタスクを作成するときは、適切な名前の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 __== "__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()だけに置き換えて、違いを感じることができます。

93

create_task()

  • コルーチンを受け入れ、
  • taskを返します。
  • ループのコンテキストで呼び出されます。

ensure_future()

  • 先物、コルーチン、待機可能なオブジェクト、
  • task(またはFutureが渡された場合はFuture)を返します。
  • 指定された引数がコルーチンの場合、create_taskを使用します。
  • ループオブジェクトを渡すことができます。

ご覧のとおり、create_taskはより具体的です。


create_taskまたはensure_futureのないasync関数

async関数を呼び出すと、コルーチンが返されます

>>> async def doit(i):
...     await asyncio.sleep(3)
...     return i
>>> doit(4)   
<coroutine object doit at 0x7f91e8e80ba0>

そして、内部の gather により、引数がフューチャーであることを(ensure_future)保証するため、明示的にensure_futureは冗長です。

同様の質問 loop.create_task、asyncio.async/ensure_futureとTaskの違いは何ですか?

36
kwarunek

注:Python 3.7でのみ有効です(Python 3.5の場合は 以前の回答 を参照してください)。

公式ドキュメントから:

asyncio.create_task (Python 3.7に追加)は、 ensure_future() ではなく、新しいタスクを生成するための望ましい方法です。


詳細:

したがって、Python 3.7以降では、2つのトップレベルラッパー関数があります(類似しているが異なります)。

結局のところ、これらのラッパー関数は両方とも BaseEventLoop.create_task の呼び出しに役立ちます。唯一の違いは、ensure_futureawaitable オブジェクトを受け入れ、それをFutureに変換するのに役立つことです。また、独自のevent_loopパラメーターをensure_futureで提供することもできます。これらの機能が必要かどうかに応じて、使用するラッパーを選択するだけです。

11
Yeo

たとえば、3つのタイプはすべて非同期で実行されます。唯一の違いは、3番目の例では、10個すべてのコルーチンを事前に生成し、一緒にループに送信することです。そのため、最後のものだけがランダムに出力します。

3
ospider