协程与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函数来封装协程并返回一个FutureTask对象。 当您调用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,可能有所帮助:

当然,您可以在官方手册中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关于如何批处理协程/期货的明确解释时,我会更新这个答案。