我已经看过几个关于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
变量的三个变体都达到了相同的结果;我能看到的唯一区别是,对于第三个变体,执行是乱序的(在大多数情况下这应该无关紧要)。还有其他区别吗?有些情况下我不能使用最简单的变体(协同列表)吗?
答案 0 :(得分:88)
为此目的从Python 3.7 asyncio.create_task(coro)
高级函数was added开始。
您应该使用它来代替从coroutimes创建任务的其他方法。但是,如果您需要从任意等待创建任务,则应使用asyncio.ensure_future(obj)
。
ensure_future
vs create_task
ensure_future
是一种从Task
创建coroutine
的方法。它基于参数以不同的方式创建任务(包括对协程和类似未来的对象使用create_task
)。
create_task
是AbstractEventLoop
的抽象方法。不同的事件循环可以以不同的方式实现此功能。
您应该使用ensure_future
来创建任务。只有在您要实施自己的事件循环类型时,您才需要create_task
。
<强> UPD:强>
@ bj0在Guido's answer上指出了这个主题:
ensure_future()
的意思是,如果你有可能的东西 要么是协程,要么是Future
(后者包括Task
因为 那是Future
)的子类,你希望能够调用一个方法 在它上面只定义在Future
上(可能是唯一有用的 例如cancel()
)。当它已经是Future
(或Task
)时 什么也没做;如果它是一个协程,它将其包裹在Task
中。如果你知道你有一个协程并且你希望它被安排, 要使用的正确API是
create_task()
。你应该唯一的时间 正在调用ensure_future()
就是在提供API时(就像大多数人一样) asyncio自己的API)接受协程或Future
和 你需要做一些事情,要求你拥有Future
。
以后:
最后我仍然相信
ensure_future()
是恰当的 一个不太需要的功能名称的模糊名称。创建时 来自协程的任务你应该使用适当命名的loop.create_task()
。也许应该有一个别名asyncio.create_task()
?
令我惊讶的是。我一直使用ensure_future
的主要动机是,与循环的成员create_task
相比,它是更高级别的功能(讨论contains一些想法,例如添加{{1} }或asyncio.spawn
)。
我还可以指出,在我看来,使用可以处理任何asyncio.create_task
而不是协程的通用函数非常方便。
然而,Guido的答案很明确:&#34;从协程创建任务时,您应该使用名称恰当的Awaitable
&#34;
在任务中包装协程 - 是一种在背景中启动此协程的方法&#34;。这是一个例子:
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())
以感受其中的差异。
答案 1 :(得分:33)
create_task()
ensure_future()
create_task
,正如您所看到的,create_task更具体。
async
函数没有create_task或ensure_future 简单调用async
函数返回coroutine
>>> async def doit(i):
... await asyncio.sleep(3)
... return i
>>> doit(4)
<coroutine object doit at 0x7f91e8e80ba0>
由于引擎盖下的gather
确保(ensure_future
)args是未来,因此明确ensure_future
是多余的。
类似问题What's the difference between loop.create_task, asyncio.async/ensure_future and Task?
答案 2 :(得分:10)
注意:仅对 Python 3.7 有效(对于Python 3.5,请参考earlier answer)。
摘自官方文档:
asyncio.create_task
(Python 3.7中已添加)是生成新任务的替代方法,而不是ensure_future()
。
因此,现在,在Python 3.7及更高版本中,有2个顶级包装函数(相似但不同):
asyncio.create_task
:直接调用event_loop.create_task(coro)
。 (see source code)ensure_future
,如果它是协程,它也称为event_loop.create_task(coro)
,只是确保返回类型为asyncio.Future即可。 (see source code)。无论如何,Task
由于其类继承(ref)仍然是Future
。 好吧,这两个包装函数都可以帮助您调用BaseEventLoop.create_task
。唯一的区别是ensure_future
接受任何awaitable
对象,并帮助您将其转换为Future。您也可以在event_loop
中提供自己的ensure_future
参数。而且,根据您是否需要这些功能,您可以简单地选择要使用的包装器。
答案 3 :(得分:3)
对于您的示例,所有三种类型都是异步执行的。唯一的区别是,在第三个例子中,你预先生成了所有10个协程,并一起提交给循环。所以只有最后一个随机提供输出。