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

cru*_*rky 81 python coroutine python-3.x python-asyncio python-3.5

我已经看过几个关于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)
Run Code Online (Sandbox Code Playgroud)

上面定义futures变量的所有三个变体都实现了相同的结果; 我能看到的唯一区别是,对于第三个变体,执行是乱序的(在大多数情况下这应该无关紧要).还有其他区别吗?有些情况下我不能只使用最简单的变体(协程的简单列表)吗?

Mik*_*mov 98

实际信息:

从Python 3.7开始,为此目的添加asyncio.create_task(coro)高级功能.

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


旧信息:

ensure_future VS create_task

ensure_future是创建一个方法Taskcoroutine.它基于参数以不同的方式创建任务(包括使用create_task协同程序和类似未来的对象).

create_task是一种抽象的方法AbstractEventLoop.不同的事件循环可以不同的方式实现此功能.

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

UPD:

@ bj0指出了Guido关于这个主题的答案:

关键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(讨论包含一些想法,如添加asyncio.spawnasyncio.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())
Run Code Online (Sandbox Code Playgroud)

输出:

first
long_operation started
second
long_operation finished
Run Code Online (Sandbox Code Playgroud)

您可以替换asyncio.ensure_future(long_operation())只是await long_operation()为了感受差异.

  • 根据 Guido 的说法,如果您确实需要一个通常不需要的任务对象,则应该使用 `create_task`:https://github.com/python/asyncio/issues/477#issuecomment-268709555 (4认同)
  • @laycat我们需要在msg()中的await将控制权返回到第二个调用的事件循环。一旦获得控制权,事件循环就可以启动long_operation()。它用来演示`ensure_future`如何启动协程以与当前执行流程同时执行。 (2认同)
  • @garej,如果你删除它,你不应该看到最后的输出“long_operation finish”,因为“main()”(和整个事件循环)早于“long_operation()”任务完成。如果您在 Jupyter 中运行脚本,情况可能并非如此,但无论如何,“等待任务”的想法是我们需要它来等待任务完成。 (2认同)

kwa*_*nek 41

create_task()

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

ensure_future()

  • 接受期货,协同程序,等待物品,
  • 返回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>
Run Code Online (Sandbox Code Playgroud)

而且由于gather引擎盖确保(ensure_future)args是未来,显然ensure_future是多余的.

类似的问题loop.create_task,asyncio.async/ensure_future和Task之间有什么区别?


Yeo*_*Yeo 12

注意:仅对Python 3.7有效(对于Python 3.5,请参阅前面的答案).

来自官方文档:

asyncio.create_task(在Python 3.7中添加)是产生新任务的首选方法,而不是ensure_future().


详情:

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

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