Sure_future vs. BaseEventLoop.create_task vs. simple coroutine?

我看到过一些关于异步的基本 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变量的所有三个变量都得到了相同的结果; 我能看到的唯一区别是,对于第三个变量,执行是无序的(这在大多数情况下应该没有关系)。还有别的区别吗?是否存在不能使用最简单变量(协程清单)的情况?

91240 次浏览

create_task()

  • 接受协同程序,
  • 返回 Task,
  • 它在循环的上下文中被调用。

ensure_future()

  • 接受期货,协同程序,等待对象,
  • 返回 Task (如果 Future 通过则返回 Future)。
  • 如果给定的参数是协程,则使用 create_task,
  • 循环对象。

正如您可以看到的,create _ task 更加具体。


没有 create _ task 或 sure _ future 的 async函数

简单调用 async函数返回 coroutine

>>> async def doit(i):
...     await asyncio.sleep(3)
...     return i
>>> doit(4)
<coroutine object doit at 0x7f91e8e80ba0>

而且由于引擎盖下的 gather确保(ensure_future)参数是期货,因此显式地 ensure_future是多余的。

类似的问题 什么是循环。创建 _ 任务,异步。异步/确保 _ 未来和任务之间的区别?

实际信息:

为此,从 Python 3.7 asyncio.create_task(coro)高级函数 增加了开始。

您应该使用它来代替通过 coroutimes 创建任务的其他方法。但是,如果需要从任意可等待的任务创建任务,则应使用 asyncio.ensure_future(obj)


旧信息:

ensure_future vs create_task

ensure_future是一种从 coroutine创建 Task的方法。它根据参数以不同的方式创建任务(包括对协程和类未来对象使用 create_task)。

create_taskAbstractEventLoop的一种抽象方法,不同的事件循环可以以不同的方式实现这个功能。

您应该使用 ensure_future来创建任务。只有在实现自己的事件循环类型时才需要 create_task

更新:

@ bj0向 圭多的回答指出:

ensure_future()的意义在于如果你有什么东西 可以是协同程序,也可以是 Future(后者包括 Task,因为 这是 Future的一个子类,你想要能够调用一个方法 只有在 Future上定义(可能是唯一有用的 例子是 cancel())。当它已经是一个 Future(或 Task)这 什么也不做; 当它是一个协同程序时,它在 Task中是 包裹

如果你知道你有一个协同程序,你想要它被安排, 正确使用的 API 是 create_task() 调用 ensure_future()是指提供一个 API (与大多数 的 API) ,它接受协程或 Future,并且 你需要做的东西,需要你有一个 Future

后来:

最后我还是认为 ensure_future()是一个适当的 一个很少需要的功能的模糊名称 来自协同程序的任务,应该使用适当命名的 loop.create_task()也许应该有个化名 asyncio.create_task()

我很惊讶。我一直使用 ensure_future的主要动机是,与循环的成员 create_task相比,它是一个更高级的函数(讨论 包含的一些想法,比如添加 asyncio.spawnasyncio.create_task)。

我还可以指出,在我看来,使用能够处理任何 Awaitable而不仅仅是协程的通用函数是相当方便的。

然而,圭多的回答很明确: ”从协同程序创建任务时,应使用适当命名的 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

你可以用 await long_operation()代替 asyncio.ensure_future(long_operation())来感受这种差异。

对于您的示例,所有这三种类型都是异步执行的。唯一的区别是,在第三个示例中,您预先生成了所有10个协程,并一起提交给循环。所以只有最后一个随机输出。

注意: 只对 Python 3.7有效(对于 Python 3.5,请参考 早期答案)。

官方文件显示:

asyncio.create_task (在 Python 3.7中添加)是产生新任务而不是 ensure_future()的更好方法。


详情:

所以现在,在 Python 3.7之后,有两个顶级包装函式(类似但不同) :

好的,最终这两个包装函数都将帮助您调用 BaseEventLoop.create_task。唯一的区别是 ensure_future接受任何 awaitable对象并帮助您将其转换为 Future。你也可以在 ensure_future中提供你自己的 event_loop参数。根据您是否需要这些功能,您可以简单地选择使用哪个包装器。