You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于Asyncio实现带异步生成器的并发生产者-消费者流

解决方案:异步并发处理AsyncGenerator输入并实时产出结果

要实现对每个item的slow_task并发处理,且结果就绪即产出、无需保持输入顺序,可以利用asyncio.create_task和asyncio.as_completed实现,无需复杂的生产者/消费者队列(队列方案也可行,但前者更简洁)。

优化后的代码实现

from collections.abc import AsyncGenerator
import asyncio

async def input_gen() -> AsyncGenerator[str, None]:
    '''a simple generator that yields strings'''
    for char in "abc123xyz789":
        await asyncio.sleep(0.1)
        yield char


async def slow_task(item: str) -> str:
    '''simulate a slow task that operates on a single item'''
    await asyncio.sleep(0.5)
    return f"{item}_loaded"


async def my_gen() -> AsyncGenerator[str, None]:
    '''并发处理每个item,结果就绪即产出'''
    tasks = set()
    
    # 一边读取输入,一边提交任务并处理已完成的结果
    async for item in input_gen():
        task = asyncio.create_task(slow_task(item))
        tasks.add(task)
        
        # 迭代已完成的任务,实时产出结果
        for done_task in asyncio.as_completed(tasks.copy()):
            result = await done_task
            yield result
            tasks.remove(done_task)
    
    # 输入耗尽后,处理剩余未完成的任务
    for done_task in asyncio.as_completed(tasks):
        result = await done_task
        yield result

# 测试执行
async def main():
    results = [x async for x in my_gen()]
    print(results)

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

核心逻辑说明

  • 并发任务提交:每从input_gen拿到一个item,就用asyncio.create_task将slow_task(item)包装成异步任务并加入集合,实现多任务并行执行。
  • 实时结果产出:通过asyncio.as_completed监控任务集合,一旦有任务完成就立即获取结果并yield,满足“就绪即输出”的要求。
  • 收尾处理:当input_gen遍历结束后,继续处理剩余未完成的任务,确保所有结果都能被产出。

效率对比

原串行代码总耗时约7.2秒(12个item × (0.1秒输入间隔 + 0.5秒任务耗时)),优化后的并发代码总耗时约1.6秒(最后一个item的输入间隔1.1秒 + 0.5秒任务耗时),效率提升明显。

备选队列方案(适用于需要并发数控制的场景)

如果需要限制并发任务数量(避免系统资源耗尽),可以用asyncio.Queue实现生产者-消费者模式:

from collections.abc import AsyncGenerator
import asyncio

async def input_gen() -> AsyncGenerator[str, None]:
    for char in "abc123xyz789":
        await asyncio.sleep(0.1)
        yield char

async def slow_task(item: str) -> str:
    await asyncio.sleep(0.5)
    return f"{item}_loaded"

async def consumer(input_q: asyncio.Queue, output_q: asyncio.Queue):
    while True:
        item = await input_q.get()
        try:
            result = await slow_task(item)
            await output_q.put(result)
        finally:
            input_q.task_done()

async def my_gen(concurrency: int = 5) -> AsyncGenerator[str, None]:
    input_q = asyncio.Queue()
    output_q = asyncio.Queue()
    
    # 启动指定数量的消费者任务
    consumers = [asyncio.create_task(consumer(input_q, output_q)) for _ in range(concurrency)]
    
    # 生产者:将输入生成器的item放入队列
    async def producer():
        async for item in input_gen():
            await input_q.put(item)
        await input_q.join()  # 等待所有输入任务被处理
        await output_q.put(None)  # 发送结束信号
    
    asyncio.create_task(producer())
    
    # 从输出队列拿结果,直到收到结束信号
    while True:
        result = await output_q.get()
        if result is None:
            break
        yield result
    
    # 关闭消费者任务
    for task in consumers:
        task.cancel()
    await asyncio.gather(*consumers, return_exceptions=True)

async def main():
    results = [x async for x in my_gen()]
    print(results)

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

该方案通过concurrency参数限制并发数,适合输入量极大的场景。

内容的提问来源于stack exchange,提问作者jhamman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 06:53:26