asyncio.ensure_future与BaseEventLoop.create_task对比简单的协同程序?

时间:2016-03-31 20:15:20

标签: python python-3.x python-3.5 coroutine python-asyncio

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

4 个答案:

答案 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_taskAbstractEventLoop的抽象方法。不同的事件循环可以以不同的方式实现此功能。

您应该使用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()

  • 接受Futures,coroutines,awaitable objects,
  • 返回Task(如果Future传递,则返回Future)。
  • 如果给定的arg是协程,它使用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个顶级包装函数(相似但不同):

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

答案 3 :(得分:3)

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