さまざまなフレーバーで同じ操作を行う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番目のバリアントでは実行が順不同であることです(ほとんどの場合、これは問題ではありません)。他に違いはありますか?最も単純なバリアント(コルーチンの単純なリスト)だけを使用できない場合はありますか?
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()
だけに置き換えて、違いを感じることができます。
create_task()
ensure_future()
create_task
を使用します。ご覧のとおり、create_taskはより具体的です。
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の違いは何ですか?
注:Python 3.7でのみ有効です(Python 3.5の場合は 以前の回答 を参照してください)。
公式ドキュメントから:
asyncio.create_task
(Python 3.7に追加)は、ensure_future()
ではなく、新しいタスクを生成するための望ましい方法です。
したがって、Python 3.7以降では、2つのトップレベルラッパー関数があります(類似しているが異なります)。
asyncio.create_task
: event_loop.create_task(coro)
を直接呼び出すだけです。 ( ソースコードを参照 )ensure_future
これも呼び出します event_loop.create_task(coro)
それがコルーチンである場合、または戻り値の型が asyncio.Future であることを確認するだけです。 ( ソースコードを参照 )。とにかく、Task
は、クラスの継承( ref )により、まだFuture
のままです。結局のところ、これらのラッパー関数は両方とも BaseEventLoop.create_task
の呼び出しに役立ちます。唯一の違いは、ensure_future
が awaitable
オブジェクトを受け入れ、それをFutureに変換するのに役立つことです。また、独自のevent_loop
パラメーターをensure_future
で提供することもできます。これらの機能が必要かどうかに応じて、使用するラッパーを選択するだけです。
たとえば、3つのタイプはすべて非同期で実行されます。唯一の違いは、3番目の例では、10個すべてのコルーチンを事前に生成し、一緒にループに送信することです。そのため、最後のものだけがランダムに出力します。