asyncio.ensure_future vs. BaseEventLoop.create_task vs. 简单协程?

新手上路,请多包涵

我看过几个关于 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 变量的所有三个变体都获得了相同的结果;我能看到的唯一区别是,对于第三个变体,执行是乱序的(在大多数情况下这无关紧要)。还有其他区别吗?是否存在我不能只使用最简单的变体(协程的简单列表)的情况?

原文由 crusaderky 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.3k
2 个回答

实际信息:

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

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


旧信息:

ensure_future 对比 create_task

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

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

您应该使用 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 __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() 以感受差异。

原文由 Mikhail Gerasimov 发布,翻译遵循 CC BY-SA 4.0 许可协议

create_task()

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

ensure_future()

  • 接受 Futures、coroutines、可等待的对象,
  • 返回 Task(如果 Future 通过,则返回 Future)。
  • 如果给定的 arg 是协程,它使用 create_task
  • 可以传递循环对象。

如您所见,create_task 更具体。


async 没有 create_task 或 ensure_future 的函数

简单调用 async 函数返回协程

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

由于引擎盖下的 gather 确保( ensure_future )args 是期货,明确 ensure_future 是多余的。

类似问题 loop.create_task、asyncio.async/ensure_future 和 Task 有什么区别?

原文由 kwarunek 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题