asyncio.ensure_future vs. BaseEventLoop.create_task vs. simple coroutine?
我看过一些关于asyncio的基本Python 3.5教程,以各种方式进行相同的操作。
在此代码中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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) |
上面所有定义
实际信息:
为此,从Python 3.7
您应该使用它代替其他从coroutime创建任务的方法。但是,如果需要从任意等待中创建任务,则应使用
旧信息:
您应该使用
更新:
@ bj0指向Guido对此主题的回答:
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 wraps it in aTask .If you know that you have a coroutine and you want it to be scheduled,
the correct API to use iscreate_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 .
然后:
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-named
loop.create_task() . Maybe there should be an alias for that
asyncio.create_task() ?
我很惊讶我一直使用
我还可以指出,我认为使用可以处理任何
但是,Guido的答案很明确:"从协程创建任务时,应使用适当命名的
什么时候应该将协程包裹在任务中?
将协程包装在任务中-是一种在后台启动协程的方法。例子如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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()) |
输出:
1 2 3 4 | first long_operation started second long_operation finished |
您可以仅用
- 接受协程,
- 返回任务,
- 它在循环上下文中调用。
- 接受期货,协程,等待对象,
- 返回Task(如果Future通过,则返回Future)。
-
如果给定的arg是协程,则使用
create_task , - 可以传递循环对象。
如您所见,create_task更具体。
简单调用
1 2 3 4 5 | >>> async def doit(i): ... await asyncio.sleep(3) ... return i >>> doit(4) <coroutine object doit at 0x7f91e8e80ba0> |
并且由于引擎盖下的
类似的问题loop.create_task,asyncio.async / ensure_future和Task之间有什么区别?
Note: Only valid for Python 3.7 (for Python 3.5 refer to the earlier answer).
从官方文档:
asyncio.create_task (added in Python 3.7) is the preferable way for spawning new tasks instead ofensure_future() .
详情:
因此,现在,在Python 3.7及更高版本中,有2个顶级包装函数(相似但不同):
-
asyncio.create_task :直接调用event_loop.create_task(coro) 。 (请参见源代码) -
ensure_future 如果它是协程,也可以调用event_loop.create_task(coro) ,只是确保返回类型为asyncio.Future。 (请参见源代码)。无论如何,由于Task 的类继承(参考),它仍然是Future 。
好吧,这两个包装器函数最好都可以帮助您调用
对于您的示例,所有这三种类型都是异步执行的。唯一的区别是,在第三个示例中,您预先生成了所有10个协程,然后一起提交给循环。因此只有最后一个随机输出。