1. ホーム
  2. python

[解決済み] asyncio.ensure_future vs. BaseEventLoop.create_task vs. simple coroutine?

2022-05-13 17:46:58

質問

私は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 番目のバリエーションでは実行が順番通りでないことです (ほとんどの場合、これは問題ではないはずです)。他に何か違いがあるのでしょうか?最も単純なバリアント(コルーチンのプレーンなリスト)を使用できないケースはあるのでしょうか?

どのように解決するのですか?

実際の情報です。

Python 3.7からスタート asyncio.create_task(coro) 高レベル関数 が追加されました。 が追加されました。

コアタイムからタスクを生成する他の方法の代わりに、この方法を使う必要があります。ただし、任意の待ち行列からタスクを生成する必要がある場合には asyncio.ensure_future(obj) .


古い情報です。

ensure_futurecreate_task

ensure_future Task から coroutine . 引数に応じた様々な方法でタスクを作成します(例えば create_task を使うなど)。

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

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

更新しました。

@bj0 が指摘した グイドの答え を参照してください。

<ブロッククオート

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

コルーチンがあることが分かっていて、それをスケジュールしたい場合。 を使うのが正しいAPIです。 create_task() . を使うべき唯一の場合です。 を呼び出す必要があります。 ensure_future() を呼び出す必要があるのは、(asyncio 自身の API のような) API を提供するときだけです。 asyncio自身のAPIのように)コルーチンまたは Future と を必要とするような 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 __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() で違いを実感してください。