协程与Python 3.5中未来/任务的区别?
假设我们有一个虚拟函数:
async def foo(arg): result = await some_remote_call(arg) return result.upper()
有什么区别:
coros = [] for i in range(5): coros.append(foo(i)) loop = get_event_loop() loop.run_until_complete(wait(coros))
和:
from asyncio import ensure_future futures = [] for i in range(5): futures.append(ensure_future(foo(i))) loop = get_event_loop() loop.run_until_complete(wait(futures))
注意 :该示例返回结果,但这不是问题的重点。 当返回值很重要时,使用gather()
而不是wait()
。
无论返回值如何,我正在寻找ensure_future()
清晰度。 wait(coros)
和wait(coros)
两者都运行协程,那么何时以及为什么要将协程打包在ensure_future
?
基本上,使用Python 3.5的async
运行一堆非阻塞操作的正确方法是什么?
额外的信用,如果我想批量打电话? 例如,我需要调用some_remote_call(...)
1000次,但我不想用1000个同时连接粉碎web服务器/数据库/等。 这是可行的线程或进程池,但有没有办法用asyncio
做到这asyncio
?
协程是一个生成器函数,既可以产生值,也可以接受来自外部的值。 使用协程的好处是我们可以暂停一个函数的执行并在稍后恢复。 在networking操作的情况下,当我们等待响应时,暂停执行一个函数是有意义的。 我们可以用这个时间来运行一些其他的function。
未来就像来自Javascript的Promise
对象。 它就像是一个将来会实现的价值的占有者。 在上面提到的情况下,在等待networkingI / O的时候,一个函数可以给我们一个容器,一个承诺,当操作完成时它会填充容器的值。 我们坚持未来的目标,当它满足时,我们可以调用一个方法来检索实际的结果。
直接回答:如果您不需要结果,则不需要ensure_future
。 如果您需要结果或检索exception,它们是很好的。
额外的积分:我会selectrun_in_executor
并传递一个Executor
实例来控制最大工作者的数量。
说明和示例代码
在第一个例子中,你正在使用协程。 wait
函数需要一系列协程并将它们组合在一起。 所以wait()
在所有的coroutine耗尽时(完成/完成返回所有的值)结束。
loop = get_event_loop() # loop.run_until_complete(wait(coros))
run_until_complete
方法将确保循环处于活动状态,直到执行完成。 请注意,在这种情况下,您没有得到asynchronous执行的结果。
在第二个示例中,您使用ensure_future
函数来封装协程并返回一个Future
的Task
对象。 当您调用ensure_future
时,协程计划在主事件循环中执行。 返回的未来/任务对象还没有值,但随着时间的推移,当networking操作完成时,未来的对象将保持操作的结果。
from asyncio import ensure_future futures = [] for i in range(5): futures.append(ensure_future(foo(i))) loop = get_event_loop() loop.run_until_complete(wait(futures))
所以在这个例子中,除了我们使用的是期货而不是使用协同程序之外,我们正在做同样的事情。
让我们来看看如何使用asyncio / coroutines / futures的例子:
import asyncio async def slow_operation(): await asyncio.sleep(1) return 'Future is done!' def got_result(future): print(future.result()) # We have result, so let's stop loop.stop() loop = asyncio.get_event_loop() task = loop.create_task(slow_operation()) task.add_done_callback(got_result) # We run forever loop.run_forever()
在这里,我们在loop
对象上使用了create_task
方法。 ensure_future
会在主事件循环中安排任务。 这个方法使我们能够在我们select的循环上安排一个协程。
我们还看到了在任务对象上使用add_done_callback
方法添加callback的概念。
当协程返回一个值,引发exception或被取消时, Task
done
。 有办法来检查这些事件。
我已经写了一些关于这些主题的博客post,可能有所帮助:
- http://masnun.com/2015/11/13/python-generators-coroutines-native-coroutines-and-async-await.html
- http://masnun.com/2015/11/20/python-asyncio-future-task-and-the-event-loop.html
- http://masnun.com/2015/12/07/python-3-using-blocking-functions-or-codes-with-asyncio.html
当然,您可以在官方手册中find更多详细信息: https : //docs.python.org/3/library/asyncio.html
文森特的评论链接到https://github.com/python/asyncio/blob/master/asyncio/tasks.py#L346 ,它显示wait()
ensure_future()
为您包装了ensure_future()
的协程!
换句话说,我们确实需要一个未来,而协程将被默默地转化成它们。
当我find关于如何批处理协程/期货的明确解释时,我会更新这个答案。