在异步中为循环并行化

时间:2019-10-04 05:03:00

标签: python python-asyncio

我目前有如下的for循环

async def process(tasks, num):
      count = 0
      results = []
      for task in tasks:
           if count >= num:
               break
           result = await some_async_task(task)
           if result == 'foo':
               continue
           results.append(result)
           count+=1

我想知道我是否可以在这里使用collect或wait_for原语。但是我不确定如何在其中实现这些逻辑?就像..如果count> = num,我不想不必要地等待任务。 如果有20个任务并且num = 4,那么我不想运行所有20个任务。

2 个答案:

答案 0 :(得分:2)

您可以按等于所需数量的结果来批量处理任务。如果将此类批处理分配给asyncio.gather(),它将同时运行它们并保留结果的顺序。例如:

async def process(tasks, num):
    results = []
    task_iter = iter(tasks)
    while len(results) < num:
        next_batch = tuple(itertools.islice(task_iter, num - len(results)))
        if len(next_batch) == 0:
            break
        batch_results = await asyncio.gather(*next_batch)
        results.extend(r for r in batch_results if r == 'foo')

答案 1 :(得分:1)

使用aiostream library可以轻松实现。这是一个工作示例:

import asyncio
from random import random
from aiostream import stream, pipe


async def some_async_task(i):
    await asyncio.sleep(random())
    return i if random() < 0.2 else None


async def process(task_args, n):
    return await (
        stream.iterate(task_args)
        | pipe.map(some_async_task, task_limit=n)
        | pipe.filter(bool)
        | pipe.take(n)
        | pipe.list()
    )


async def main():
    print(await process(task_args=range(100), n=10))


if __name__ == "__main__":
    asyncio.run(main())

程序将打印成功的前10个任务的列表:

[1, 8, 16, 18, 19, 37, 42, 43, 45, 47]

还要注意,您可以使用some_async_task参数调整可以同时运行的task_limit的数量。

免责声明:我是项目维护者。