Python3.5中协同程序和未来/任务之间的区别?

Python3.5中协同程序和未来/任务之间的区别?,python,python-3.x,python-asyncio,Python,Python 3.x,Python Asyncio,假设我们有一个虚拟函数: async def foo(arg): result = await some_remote_call(arg) return result.upper() 这两者之间的区别是什么: import asyncio coros = [] for i in range(5): coros.append(foo(i)) loop = asyncio.get_event_loop() loop.run_until_complete(asyn

假设我们有一个虚拟函数:

async def foo(arg):
    result = await some_remote_call(arg)
    return result.upper()
这两者之间的区别是什么:

import asyncio    

coros = []
for i in range(5):
    coros.append(foo(i))

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(coros))
以及:

注意:该示例返回一个结果,但这不是问题的重点。如果返回值很重要,请使用
gather()
而不是
wait()

不管返回值如何,我都希望
sure\u future()
更清晰
wait(coros)
wait(futures)
都运行协同程序,那么何时以及为什么要在
中包装协同程序以确保未来

基本上,使用Python3.5的
async
运行一系列非阻塞操作的正确方法(tm)是什么

为了获得额外的积分,如果我想成批打电话怎么办?例如,我需要调用
一些远程调用(…)
1000次,但我不想用1000个同时连接破坏web服务器/数据库/etc。这在线程或进程池中是可行的,但是有没有办法在
asyncio
中实现这一点

2020更新(Python 3.7+):不要使用这些代码段。而是使用:

import asyncio

async def do_something_async():
    tasks = []
    for i in range(5):
        tasks.append(asyncio.create_task(foo(i)))
    await asyncio.gather(*tasks)

def do_something():
    asyncio.run(do_something_async)

还考虑使用AssiCIO的一个强大的第三方替代。

< P>一个协同程序是一个生成器函数,它既可以产生值,也可以从外部接受值。使用协同程序的好处是,我们可以暂停函数的执行,稍后再继续执行。在网络操作的情况下,在等待响应时暂停函数的执行是有意义的。我们可以利用这段时间运行其他一些函数

未来就像Javascript中的
Promise
对象。它就像一个占位符,用于表示将来将具体化的值。在上述情况下,当等待网络I/O时,函数可以给我们一个容器,一个承诺,当操作完成时,它将用值填充容器。我们保留未来的对象,当它实现时,我们可以调用它的方法来检索实际结果

直接回答:如果您不需要结果,您不需要
确保未来。如果需要结果或检索发生的异常,它们是很好的

额外积分:我会选择并传递一个
Executor
实例来控制最大工作人数

解释和示例代码 在第一个示例中,您使用的是协同程序。
wait
函数接受一组协程并将它们组合在一起。所以
wait()

run_until_complete
方法将确保循环在执行完成之前处于活动状态。请注意,在这种情况下,您如何无法获得异步执行的结果

在第二个示例中,您使用
sure\u future
函数包装一个协同程序,并返回一个
Task
对象,它是一种
future
。当您调用
sure\u future
时,协程计划在主事件循环中执行。返回的future/task对象还没有值,但随着时间的推移,当网络操作完成时,future对象将保存操作的结果

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()
这里,我们在
循环
对象上使用了
create_task
方法<代码>确保未来将在主事件循环中安排任务。这种方法使我们能够在所选择的循环上调度一个协同路由

我们还看到了在task对象上使用
add\u done\u callback
方法添加回调的概念

当协同程序返回值、引发异常或被取消时,
任务
完成
。有一些方法可以检查这些事件

我写了一些关于这些主题的博客文章,可能会有所帮助:


当然,您可以在官方手册上找到更多详细信息:

Vincent的评论链接到,这表明
wait()
为您在
sure_future()
中包装了协同程序

换句话说,我们确实需要一个未来,而协同程序将被默默地转化为它们

当我找到关于如何批处理协同计划/期货的明确解释时,我将更新此答案。

任务
  • 这是一个包裹在未来的合作项目
  • 类任务是类Future的一个子类
  • 因此它也适用于等待

  • 它与裸协同程序有何不同
  • 它可以在不等待的情况下取得进展
    • 只要你在等别的东西,比如说。
      • 等待[其他东西]
考虑到这一点,
确保未来作为创建任务的名称是有意义的,因为无论您是否等待它(只要您等待某事),都将计算未来的结果。这允许事件循环在您等待其他事情时完成您的任务。请注意,在Python 3.7中,创建任务是首选方法

注:我将Guido幻灯片中的“收益率”改为“等待”,以体现现代性。

简单答案
  • 调用协程函数(
    async def
    )不会运行它。它返回一个协同程序对象,就像生成器函数返回生成器对象一样
  • wait
    从协程中检索值,即“调用”协程
  • eusure\u future/create\u task
    计划在下一次迭代时在事件循环上运行协同程序(尽管不像守护进程线程那样等待它们完成)
一些代码示例 让我们首先明确一些术语:

  • 协同程序函数,您可以
    async def
    s
  • 科罗
    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))
    
    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()
    
    import asyncio
    import time
    
    # coroutine function
    async def p(word):
        print(f'{time.time()} - {word}')
    
    
    async def main():
        loop = asyncio.get_event_loop()
        coro = p('await')  # coroutine
        task2 = loop.create_task(p('create_task'))  # <- runs in next iteration
        await coro  # <-- run directly
        await task2
    
    if __name__ == "__main__":
        loop = asyncio.get_event_loop()
        loop.run_until_complete(main())
    
    1539486251.7055213 - await
    1539486251.7055705 - create_task
    
    async def main():
        loop = asyncio.get_event_loop()
        coro = p('await')
        task2 = loop.create_task(p('create_task'))  # scheduled to next iteration
        await asyncio.sleep(1)  # loop got control, and runs task2
        await coro  # run coro
        await task2
    
    -> % python coro.py
    1539486378.5244057 - create_task
    1539486379.5252144 - await  # note the delay