Python asyncio.ensure_future 与 BaseEventLoop.create_task 与简单协程?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36342899/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
asyncio.ensure_future vs. BaseEventLoop.create_task vs. simple coroutine?
提问by crusaderky
I've seen several basic Python 3.5 tutorials on asyncio doing the same operation in various flavours. In this code:
我已经看过几个关于 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)
All the three variants above that define the futures
variable achieve the same result; the only difference I can see is that with the third variant the execution is out of order (which should not matter in most cases). Is there any other difference? Are there cases where I can't just use the simplest variant (plain list of coroutines)?
上面定义futures
变量的所有三个变体都实现了相同的结果;我能看到的唯一区别是,对于第三个变体,执行是乱序的(在大多数情况下应该无关紧要)。还有其他区别吗?是否存在我不能只使用最简单的变体(协程的简单列表)的情况?
回答by Mikhail Gerasimov
Actual info:
实际信息:
Starting from Python 3.7 asyncio.create_task(coro)
high-level function was addedfor this purpose.
为此,从 Python 3.7 开始添加了asyncio.create_task(coro)
高级函数。
You should use it instead other ways of creating tasks from coroutimes. However if you need to create task from arbitrary awaitable, you should use asyncio.ensure_future(obj)
.
您应该使用它来代替从 coroutime 创建任务的其他方式。但是,如果您需要从任意 awaitable 创建任务,则应使用asyncio.ensure_future(obj)
.
Old info:
旧资料:
ensure_future
vs create_task
ensure_future
对比 create_task
ensure_future
is a method to create Task
from coroutine
. It creates tasks in different ways based on argument (including using of create_task
for coroutines and future-like objects).
ensure_future
是一种Task
从coroutine
. 它根据参数以不同的方式创建任务(包括使用create_task
for 协程和类似未来的对象)。
create_task
is an abstract method of AbstractEventLoop
. Different event loops can implement this function different ways.
create_task
是 的抽象方法AbstractEventLoop
。不同的事件循环可以不同的方式实现这个功能。
You should use ensure_future
to create tasks. You'll need create_task
only if you're going to implement your own event loop type.
您应该使用ensure_future
来创建任务。create_task
仅当您要实现自己的事件循环类型时才需要。
Upd:
更新:
@bj0 pointed at Guido's answeron this topic:
The point of
ensure_future()
is if you have something that could either be a coroutine or aFuture
(the latter includes aTask
because that's a subclass ofFuture
), and you want to be able to call a method on it that is only defined onFuture
(probably about the only useful example beingcancel()
). When it is already aFuture
(orTask
) this does nothing; when it is a coroutine it wrapsit in aTask
.If you know that you have a coroutine and you want it to be scheduled, the correct API to use is
create_task()
. The only time when you should be callingensure_future()
is when you are providing an API (like most of asyncio's own APIs) that accepts either a coroutine or aFuture
and you need to do something to it that requires you to have aFuture
.
关键
ensure_future()
是,如果您有一些东西可以是协程或 aFuture
(后者包括 aTask
因为它是 的子类Future
),并且您希望能够调用仅在其上定义的方法Future
(可能关于唯一有用的例子是cancel()
)。当它已经是 aFuture
(orTask
) 时,它什么也不做;当它是一个协程时,它会将它包装在一个Task
.如果您知道自己有一个协程并且希望对其进行调度,那么正确使用的 API 是
create_task()
. 您应该调用的唯一时间ensure_future()
是当您提供接受协程或 a 的 API(如大多数 asyncio 自己的 API)Future
并且您需要对它做一些需要您拥有Future
.
and later:
然后:
In the end I still believe that
ensure_future()
is an appropriately obscure name for a rarely-needed piece of functionality. When creating a task from a coroutine you should use the appropriately-namedloop.create_task()
. Maybe there should be an alias for thatasyncio.create_task()
?
最后,我仍然认为这
ensure_future()
是一个很少需要的功能的适当晦涩的名称。从协程创建任务时,您应该使用适当命名的loop.create_task()
. 也许应该有一个别名asyncio.create_task()
?
It's surprising to me. My main motivation to use ensure_future
all along was that it's higher-level function comparing to loop's member create_task
(discussion containssome ideas like adding asyncio.spawn
or asyncio.create_task
).
这让我很惊讶。我一直使用的主要动机ensure_future
是与循环成员相比,它是更高级别的函数create_task
(讨论包含一些想法,如添加asyncio.spawn
或asyncio.create_task
)。
I can also point that in my opinion it's pretty convenient to use universal function that can handle any Awaitable
rather than coroutines only.
我还可以指出,在我看来,使用可以处理 anyAwaitable
而不仅仅是协程的通用函数非常方便。
However, Guido's answer is clear: "When creating a task from a coroutine you should use the appropriately-named loop.create_task()
"
但是,Guido 的回答很明确:“从协程创建任务时,您应该使用适当命名的loop.create_task()
”
When coroutines should be wrapped in tasks?
什么时候应该将协程包装在任务中?
Wrap coroutine in a Task - is a way to start this coroutine "in background". Here's example:
将协程包装在任务中 - 是一种在“后台”启动该协程的方法。下面是例子:
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())
Output:
输出:
first
long_operation started
second
long_operation finished
You can replace asyncio.ensure_future(long_operation())
with just await long_operation()
to feel the difference.
您可以替换asyncio.ensure_future(long_operation())
为只是await long_operation()
为了感受不同。
回答by kwarunek
create_task()
create_task()
- accepts coroutines,
- returns Task,
- it is invoked in context of the loop.
- 接受协程,
- 返回任务,
- 它在循环的上下文中被调用。
ensure_future()
ensure_future()
- accepts Futures, coroutines, awaitable objects,
- returns Task (or Future if Future passed).
- if the given arg is a coroutine it uses
create_task
, - loop object can be passed.
- 接受期货、协程、等待对象、
- 返回 Task (或 Future 如果 Future 通过)。
- 如果给定的 arg 是它使用的协程
create_task
, - 可以传递循环对象。
As you can see the create_task is more specific.
如您所见,create_task 更为具体。
async
function without create_task or ensure_future
async
没有 create_task 或 ensure_future 的函数
Simple invoking async
function returns coroutine
简单调用async
函数返回协程
>>> async def doit(i):
... await asyncio.sleep(3)
... return i
>>> doit(4)
<coroutine object doit at 0x7f91e8e80ba0>
And since the gather
under the hood ensures (ensure_future
) that args are futures, explicitly ensure_future
is redundant.
并且由于gather
幕后确保 ( ensure_future
) args 是期货,因此明确ensure_future
是多余的。
Similar question What's the difference between loop.create_task, asyncio.async/ensure_future and Task?
类似问题loop.create_task, asyncio.async/ensure_future 和 Task 有什么区别?
回答by Yeo
Note: Only valid for Python 3.7(for Python 3.5 refer to the earlier answer).
注意:仅对Python 3.7有效(对于 Python 3.5,请参阅较早的答案)。
From the official docs:
来自官方文档:
asyncio.create_task
(added in Python 3.7) is the preferable way for spawning new tasks instead ofensure_future()
.
asyncio.create_task
(在 Python 3.7 中添加)是生成新任务而不是ensure_future()
.
Detail:
细节:
So now, in Python 3.7 onwards, there are 2 top-level wrapper function (similar but different):
所以现在,在 Python 3.7 以后,有 2 个顶级包装函数(相似但不同):
asyncio.create_task
: which simply callevent_loop.create_task(coro)
directly. (see source code)ensure_future
which also callevent_loop.create_task(coro)
if it is coroutine or else it is simply to ensure the return type to be a asyncio.Future. (see source code). Anyway,Task
is still aFuture
due to its class inheritance (ref).
asyncio.create_task
: 直接调用event_loop.create_task(coro)
即可。(见源代码)ensure_future
event_loop.create_task(coro)
如果它是协程,它也会调用,否则它只是为了确保返回类型为asyncio.Future。(见源代码)。无论如何,由于它的类继承(ref)Task
仍然是一个。Future
Well, utlimately both of these wrapper functions will help you call BaseEventLoop.create_task
. The only difference is ensure_future
accept any awaitable
object and help you convert it into a Future. And also you can provide your own event_loop
parameter in ensure_future
. And depending if you need those capability or not, you can simply choose which wrapper to use.
好吧,最终这两个包装函数都将帮助您调用BaseEventLoop.create_task
. 唯一的区别是ensure_future
接受任何awaitable
对象并帮助您将其转换为 Future。您也可以event_loop
在ensure_future
. 根据您是否需要这些功能,您可以简单地选择要使用的包装器。
回答by ospider
for your example, all the three types execute asynchronously. the only difference is that, in the third example, you pre-generated all 10 coroutines, and submitted to the loop together. so only the last one gives output randomly.
对于您的示例,所有三种类型都是异步执行的。唯一的区别是,在第三个示例中,您预先生成了所有 10 个协程,并一起提交给循环。所以只有最后一个随机给出输出。