当前位置: 首页 > news >正文

协程(coroutine)与生成器(generator)的底层实现有何异同?


协程(coroutine)与生成器(generator)在 Python 中看似相似(均使用 yieldawait 暂停执行),但底层实现和设计目标存在显著差异。以下从执行机制内存管理应用场景三个维度解析其异同:


1. 底层实现的演变与核心差异

(1) 生成器(Generator)
  • 本质:通过 yield 关键字实现的惰性迭代器,用于按需生成数据。
  • 实现机制
    • 生成器函数被调用时,返回一个生成器对象(类型为 generator)。
    • 生成器对象内部维护一个栈帧(frame),保存局部变量和执行位置(f_lasti 指针)。
    • 每次调用 next()send() 时,恢复执行到 yield 处,并挂起当前状态。
  • 底层数据结构
    # 生成器对象的 C 结构(CPython 源码示例)
    typedef struct {
        PyObject_HEAD
        PyFrameObject *gi_frame;          # 当前执行栈帧
        PyObject *gi_code;                # 字节码对象
        PyObject *gi_weakreflist;          # 弱引用列表
        // ...
    } PyGenObject;
    
(2) 协程(Coroutine)
  • 本质:通过 async/await 实现的异步任务调度单元,用于非阻塞并发。
  • 实现机制
    • 协程函数(async def)被调用时,返回一个协程对象(类型为 coroutine)。
    • 协程对象依赖**事件循环(Event Loop)**调度,通过 await 挂起并让出控制权。
    • 底层基于**生成器协程(Python 3.4 的 @asyncio.coroutine)**演化,但优化了异步语义。
  • 底层数据结构
    # 协程对象的 C 结构(CPython 源码示例)
    typedef struct {
        PyObject_HEAD
        PyObject *cr_origin;              # 协程创建位置(调试用)
        PyObject *cr_frame;               # 协程栈帧
        PyObject *cr_code;                # 字节码对象
        // ...
    } PyCoroObject;
    

2. 关键异同对比

特性生成器(Generator)协程(Coroutine)
设计目标惰性数据生成异步并发与协作式多任务
暂停/恢复机制yield 暂停,外部 next() 恢复await 暂停,事件循环调度恢复
对象类型generatorcoroutineasync_generator
执行驱动方外部调用者(手动迭代)事件循环(自动调度)
状态管理仅维护栈帧和局部变量额外维护任务状态(如 Future 对象)
异常传播通过 throw() 注入异常由事件循环统一处理异常
内存消耗轻量(单次迭代状态)较重(需维护任务链和回调)
典型应用场景大数据流处理、惰性计算高并发I/O操作(如网络请求、文件读写)

3. 执行流程的底层差异

(1) 生成器的挂起与恢复
def gen():
    yield 1
    yield 2

g = gen()
print(next(g))  # 输出1
print(next(g))  # 输出2
  • 底层操作
    1. 调用 gen() 创建生成器对象,状态为 GEN_CREATED
    2. next(g) 触发 gi_frame 执行,直到遇到 yield,状态变为 GEN_SUSPENDED
    3. 再次调用 next(g) 恢复 gi_frame 执行。
(2) 协程的调度与执行
async def coro():
    await asyncio.sleep(1)
    print("Done")

async def main():
    await coro()

asyncio.run(main())
  • 底层操作
    1. asyncio.run() 创建事件循环,调度 main() 协程。
    2. await coro() 挂起 main(),将控制权交还事件循环。
    3. 事件循环监控 asyncio.sleep(1) 的完成状态,1秒后恢复 coro() 执行。

4. 性能优化与实现细节

  • 生成器的局限性
    • 无法直接嵌套 yieldyield from 以外的异步操作。
    • 手动管理迭代流程,难以实现高并发。
  • 协程的优化
    • async/await 语法糖:将协程与生成器解耦,避免语义混淆。
    • Task 对象封装:协程被包装为 Task,由事件循环统一调度。
    • 零拷贝挂起:通过 await 直接切换协程,减少上下文切换开销。

5. 从生成器到协程的演化

  • Python 3.4:通过 @asyncio.coroutineyield from 实现协程(基于生成器)。
  • Python 3.5:引入原生协程(async def)和 await 关键字,与生成器分离。
  • Python 3.7asyncawait 成为正式关键字,协程性能进一步优化。

总结

  • 相同点:均通过暂停/恢复机制实现非阻塞执行,依赖状态保存与恢复。
  • 不同点
    • 生成器是同步的、迭代驱动的,设计目标为数据生成;
    • 协程是异步的、事件循环驱动的,设计目标为高并发任务调度。
  • 选择建议
    • 需要惰性生成数据 → 使用生成器;
    • 需高并发处理I/O密集型任务 → 使用协程(配合 asyncio)。

相关文章:

  • Mac上更改默认应用程序
  • Webpack优化前端性能
  • MySql数据库等级考试学习分享3(Day5)
  • jQuery EasyUI 扩展
  • AI预测福彩3D新模型百十个定位预测+胆码预测+杀和尾+杀和值2025年3月13日第21弹
  • uniapp中,单选按钮组回显不起作用
  • Nginx + Keepalived 高可用集群
  • Qt常见面试题合集
  • MySQL芒果报表定时更新:轻松实现数据集成
  • 2025-03-13 学习记录--C/C++-PTA 练习2-13 求N分之一序列前N项和
  • Controller方法请求类型和参数
  • SpringMVC——REST简介及入门案例
  • 前置机跟服务器的关系
  • 文件操作2
  • node.js-WebScoket心跳机制(服务器定时发送数据,检测连接状态,重连)
  • C语言刷题第三章(下)
  • WPF未来展望:紧跟技术发展趋势,探索新的可能性
  • 《基于大数据的营养果蔬推荐系统的设计与实现》开题报告
  • C++ STL—— String库
  • Vue3中 ref 与 reactive区别
  • “大国重器”、新型反隐身雷达……世界雷达展全面展示尖端装备
  • 美国务卿鲁比奥抵达会场,将参加俄乌会谈
  • 中国首艘海洋级智能科考船“同济”号试航成功,可搭载水下遥控机器人
  • 新片|《碟中谍8:最终清算》定档5月30日
  • 证监会强化上市公司募资监管七要点:超募资金不得补流、还贷
  • 董军同德国国防部长举行会谈