asyncio.ensure_future対BaseEventLoop.create_task対単純なコルーチン?


96

さまざまなフレーバーで同じ操作を行う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)

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

回答:


116

実際の情報:

この目的のために、Python 3.7以降、asyncio.create_task(coro)高水準関数が追加されました

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


古い情報:

ensure_futurecreate_task

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

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

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

更新:

@ bj0はこのトピックに関するGuidoの回答を指摘しました:

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

コルーチンがあり、それをスケジュールしたい場合は、使用する正しいAPIはcreate_task()です。呼び出す必要がensure_future()あるのは、コルーチンまたはaのいずれかを受け入れるAPI(ほとんどのasyncio独自のAPIなど)を提供Futureしていて、を持っていることを必要とする何かを実行する必要があるときだけです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 __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()ます。


3
Guidoによると、create_task通常必要のないタスクオブジェクトが本当に必要な場合は、github.com / python / asyncio / issues / 477#issuecomment-268709555を使用する必要があります
python

@ bj0このリンクをありがとう。このディスカッションの情報を追加して、回答を更新しました。
ミハイルゲラシモ

ensure_future自動的に作成追加Taskメインイベントループに?
AlQuemist、2018

@AlQuemist作成するすべてのコルーチン、フューチャー、またはタスクは、後で実行されるイベントループに自動的にバインドされます。デフォルトでは、現在のスレッドの現在のイベントループですが、loopキーワード引数を使用して他のイベントループを指定できます(ensure_futureシグネチャを参照))。
ミハイルゲラシモ

2
我々は必要@laycat await以内にmsg()2回目の呼び出しにイベントループに制御を返すこと。受信制御を開始すると、イベントループを開始できlong_operation()ます。ensure_future現在の実行フローと同時にコルーチンを実行する方法を示すために作成されました。
ミハイルゲラシモフ2018年

45

create_task()

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

ensure_future()

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

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


async create_taskまたはEnsure_futureなしの関数

単純な呼び出し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の違いは何ですか?


13

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

公式ドキュメントから:

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


詳細:

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

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


記載されていない別の違いがあると思います。ループを実行する前にasyncio.create_taskを呼び出そうとすると、asyncio.create_taskが実行中のループを想定しているため、問題が発生します。ただし、実行中のループは必須ではないため、この場合はasyncio.ensure_futureを使用できます。
coelhudo

4

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.