首页 > 解决方案 > 使用 asyncio,等待所有结果并另外取回它们的最简单方法是什么?

问题描述

for我有一个长时间运行的函数,它被具有不同参数的循环多次调用。

我想评估该asyncio库以轻松提高该功能的速度。

该函数返回一个特定的值,该值应该存储在forlist 中的循环级别resultList

我发现一些代码片段似乎满足了我“易于实现”的要求(我只需要添加@background装饰器),但我缺少两个功能:

  1. 如何在for循环级别等待,直到函数的所有执行都执行完毕?
  2. 如何在for循环级别收集函数调用的结果?

我将如何扩展此代码段?

import asyncio
import time

def background(f):
    def wrapped(*args, **kwargs):
        return asyncio.get_event_loop().run_in_executor(None, f, *args, **kwargs)

    return wrapped

@background
def your_function(argument):
    time.sleep(5)
    myReturn = 'function finished for ' + str(argument)
    print(myReturn)
    return  myReturn


if __name__ == '__main__':
    resultList = []

    for i in range(10):
        your_function(i)
        # resultList.append() 


    # loop = asyncio.get_event_loop().run_until_complete()

    print('this should only be printed once all calls to your_function() are finished')
    print("resultList=", resultList) # should contain a list of entries with 'function finished for x' 

标签: pythonpython-asyncio

解决方案


我看到您共享的代码存在一些问题。注意:我的回答是基于您对 Jan 的评论,您提到您的功能是 I/O 受限的。

  1. 如果可以的话,不要使用asyncio.get_event_loop().run_in_executor()asyncio.run() 在你的入口点使用并保持你的方法一直异步。(https://docs.python.org/3/library/asyncio-task.html#asyncio.run
  2. 您正在使用time.sleep(). 这是 sleep 的同步版本,不会模拟 asyncio。改为使用asyncio.sleep()
  3. 请注意,它run_in_executor返回一个您想要的未来对象await。由于您在同步函数中运行代码,因此比从异步函数中运行代码更复杂。

现在,通过查看您共享的代码,没有理由将其堆栈转换为asyncio. 如果有其他外部限制,或者这只是一个较大程序的片段,下面的解决方案可能会有点不同。否则,下面的解决方案很简单。

import asyncio

async def your_function(argument):
    await asyncio.sleep(5)  # note the await here (compared to time.sleep())
    my_return = f"function finished for {argument}"
    print(my_return)
    return my_return


async def async_main():
    result_list = await asyncio.gather(*(range(10)))  # gather will run it all concurrently
    print("this should only be printed once all calls to your_function() are finished")
    print(f"result_list={result_list}", result_list) # should contain a list of entries with 'function finished for x'

if __name__ == '__main__':
    asyncio.run(async_main())


推荐阅读