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

如何从零实现Python异步生成器?同步生成器转异步正确方法

核心问题排查

你写的示例代码没有实现并发,是两个低级错误导致的:

  • asyncio.sleep(0) 前漏写了await:所有异步协程只有被await才会真正执行,没加await的话这行代码只会生成一个闲置的协程对象,完全不会触发事件循环切换,等于白写。
  • 入口run()函数只等待了第一个任务,剩余两个被调度到事件循环的任务没有被显式等待,事件循环会在第一个任务跑完后直接退出,剩下的任务甚至没机会执行完。
异步生成器基础规则

不管是从零编写还是改造同步生成器,先记住异步生成器的核心定义:

  • 用async def声明的函数,内部使用yield返回值,它就是异步生成器
  • 异步生成器只能通过async for语法迭代,普通for循环无法消费
  • 异步生成器内部可以正常使用await调用异步逻辑,执行到await点时会主动让出事件循环控制权,让其他协程并发执行

从零实现一个最简单的异步生成器示例:

import asyncio

async def async_counter(stop: int):
    for i in range(stop):
        # 这里可以替换为任意异步IO操作,比如网络请求、异步读文件
        await asyncio.sleep(0.1)
        yield i
同步生成器转异步生成器的正确方式

首先要明确边界:

如果你的同步生成器内部是CPU密集型逻辑、或者包含阻塞IO操作(比如同步网络请求、同步文件读写),单纯加await切换点是没用的——阻塞逻辑运行时会直接卡住整个事件循环,根本不会触发并发。这种场景需要用loop.run_in_executor把阻塞逻辑扔到线程池/进程池执行,避免阻塞事件循环。

如果你的同步生成器逻辑本身执行速度很快,只是想在迭代间隙让出事件循环,避免长时间占用线程阻塞其他协程,改造非常简单:每次yield之后加一个await asyncio.sleep(0)即可,这个调用会立刻把控制权交还给事件循环,让其他待运行的协程先执行。

修正后的可并发运行的示例代码

#!/usr/bin/env python3
import asyncio


async def run():
    # 创建3个并发任务,用gather等待所有任务执行完成
    tasks = [
        asyncio.ensure_future(run_async_loop()),
        asyncio.ensure_future(run_async_loop()),
        asyncio.ensure_future(run_async_loop())
    ]
    await asyncio.gather(*tasks)

async def run_async_loop():
    async for i in some_async_loop():
        print(i)

async def some_async_loop():
    for i in some_loop():
        yield i
        # 必须加await才会真正触发事件循环切换
        await asyncio.sleep(0)

def some_loop():
    for i in range(10):
        yield i


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(run())

运行这段代码你会看到三个循环的输出是交替打印的,不再是一个循环跑完才跑下一个,符合预期的并发效果。

阻塞型同步生成器的包装示例

如果你的同步生成器包含阻塞操作,参考下面的包装方式,用线程池执行阻塞逻辑避免卡事件循环:

import asyncio
import time

# 带阻塞操作的同步生成器示例
def blocking_sync_gen():
    for i in range(10):
        # 模拟阻塞IO,比如同步HTTP请求、同步读大文件
        time.sleep(0.1)
        yield i

async def wrapped_async_gen():
    loop = asyncio.get_running_loop()
    gen = blocking_sync_gen()
    while True:
        try:
            # 把next迭代操作扔到默认线程池执行,不阻塞事件循环
            val = await loop.run_in_executor(None, next, gen)
            yield val
        except StopIteration:
            break

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 12:48:15