问题: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 __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
实际信息:
从Python 3.7开始,为此添加了asyncio.create_task(coro)
高级功能。
您应该使用它代替其他从Coroutime创建任务的方式。但是,如果您需要从任意等待中创建任务,则应使用asyncio.ensure_future(obj)
。
旧信息:
ensure_future
与 create_task
ensure_future
是一种Task
从创建的方法coroutine
。它基于参数(包括create_task
对协程和类似未来的对象使用of)以不同的方式创建任务。
create_task
是的抽象方法AbstractEventLoop
。不同的事件循环可以以不同的方式实现此功能。
您应该ensure_future
用来创建任务。create_task
仅在要实现自己的事件循环类型时才需要。
更新:
的要点
ensure_future()
是,如果您拥有某种可能是协程或a的东西Future
(后者包含a,Task
因为这是的子类Future
),并且您希望能够在其上调用仅在其上定义的方法Future
(可能是唯一的)有用的示例cancel()
)。当它已经是Future
(或Task
)时,则不执行任何操作;当它是一个协程时,它将它包裹在一个Task
。如果您知道有一个协程,并且希望对其进行调度,则使用的正确API是
create_task()
。唯一应该调用的时间ensure_future()
是在提供接受协程或a的API(如asyncio自己的大多数API)时,Future
您需要对其进行一些操作,要求您拥有aFuture
。
然后:
最后,我仍然相信这
ensure_future()
是一个很少需要的功能的适当模糊的名称。从协程创建任务时,应使用适当命名的loop.create_task()
。也许应该为此起别名asyncio.create_task()
?
我感到惊讶。我一直使用的主要动机ensure_future
是,与loop的成员相比,它是更高层的函数create_task
(讨论中包含了诸如add asyncio.spawn
或的想法asyncio.create_task
)。
我还可以指出,在我看来,使用可以处理任何Awaitable
而非协程的通用函数非常方便。
但是,Guido的答案很明确:“从协程创建任务时,应使用名称正确的loop.create_task()
“
什么时候应该将协程包裹在任务中?
将协程包装在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()
来感受不同。
回答 1
create_task()
- 接受协程,
- 返回任务,
- 它在循环上下文中调用。
ensure_future()
- 接受期货,协程,等待对象,
- 返回Task(如果Future通过,则返回Future)。
- 如果给定的arg是协程,则使用
create_task
, - 可以传递循环对象。
如您所见,create_task更具体。
async
没有create_task或sure_future的函数
简单的调用async
函数返回协程
>>> async def doit(i):
... await asyncio.sleep(3)
... return i
>>> doit(4)
<coroutine object doit at 0x7f91e8e80ba0>
并且由于ensure_future
)args为期货,因此明确地ensure_future
是多余的。
类似的问题loop.create_task,asyncio.async / ensure_future和Task有什么区别?
回答 2
注意:仅对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)
即可。(请参阅源代码)如果它是协程,也可以调用它,只是确保返回类型为asyncio.Future即可。(请参阅源代码)。无论如何, Task
仍然是Future
由于其类继承( ref)。
好吧,这两个包装函数最好都可以帮助您调用ensure_future
接受任何awaitable
对象并帮助您将其转换为Future。另外,您还可以在中提供自己的event_loop
参数ensure_future
。而且,根据您是否需要这些功能,您可以简单地选择要使用的包装器。
回答 3
在您的示例中,所有这三种类型都是异步执行的。唯一的区别是,在第三个示例中,您预先生成了所有10个协程,然后一起提交给循环。因此只有最后一个随机输出。