deepwzh的技术笔记
  • Golang
  • Python
  • Cpp
杂项
前端开发
  • 计算机网络
  • 操作系统
  • 数据库
云原生
Devops
AIGC
关于作者
  • Golang
  • Python
  • Cpp
杂项
前端开发
  • 计算机网络
  • 操作系统
  • 数据库
云原生
Devops
AIGC
关于作者
  • Python
  • 魔术方法
  • pep 草案
  • 内存管理
  • 学习资料

    • 学习资料
    • 进阶学习资料
  • Asyncio

    • Asyncio
    • 调度
  • 库

    • 库
    • 模板渲染相关
    • protobuf 相关
    • mysql 相关
  • cpython开发

    • cpython 开发
    • 开发环境搭建
    • CAPI
    • 提交流程
    • Pyrepl
  • 包与虚拟环境管理器

    • 包与虚拟环境管理器
    • Pip
    • Conda
    • Poetry
    • Pyenv
  • 科学计算相关

    • 科学计算相关
    • Pandas

调度

概述

Python 的 asyncio 库提供了一种基于事件循环的异步编程模型,其核心是无栈协程(Stackless Coroutine)。无栈协程具有以下特性:

  • 单线程事件循环:asyncio 在单线程上创建一个事件循环,用于调度协程的执行。
  • 协作式多任务:协程只有在主动交出控制权(如通过 await)时,事件循环才能调度其他协程。
  • 异步等待:使用 await 调用可调度的对象(如协程、Future、Task),并获取其结果。

事件循环

事件循环是 asyncio 的核心组件,负责管理和调度所有异步任务。其工作原理如下:

  1. 检查调度堆:将到期的定时任务移动到就绪堆。
  2. 处理 I/O 事件:通过 select 系统调用监听 I/O 事件,将就绪的 I/O 任务移动到就绪堆。
  3. 执行就绪任务:从就绪堆中取出任务并执行。

事件循环通过不断重复上述步骤,实现异步任务的调度和执行。


调度的对象

在 asyncio 中,协程可以调度的对象包括以下几种:

  1. 协程(Coroutine):

    • 使用 async def 定义的函数,可以通过 await 暂停和恢复执行。
    • 协程本身是一个可等待对象,但不能直接被调度,需要封装为 Task。
  2. Future:

    • 表示一个异步操作的最终结果。
    • 通常由低级 API 使用,开发者更多使用 Task。
  3. Task:

    • 是 Future 的子类,封装了一个协程。
    • 代表一个正在运行的协程,事件循环会调度其执行。

这些对象都可以通过 await 进行等待调用。

调度结构

事件循环通过两个核心数据结构管理协程任务:就绪堆和调度堆。

就绪堆(Ready Queue)

  • 作用:存储当前可以立即执行的协程任务。

  • 特点:

    • 任务按 FIFO(先进先出)的顺序执行。
    • 当协程遇到 await 并暂停时,事件循环会从就绪堆中取出下一个任务执行。
  • 加入时机:

    • 使用 call_soon() 或 call_soon_threadsafe() 时,协程会加入就绪堆。
    • I/O 事件完成或定时任务到期时,相关任务会加入就绪堆。

调度堆(Scheduled Heap)

  • 作用:存储需要延迟执行的协程任务,通常用于定时任务。

  • 特点:

    • 任务按时间戳排序,堆顶的任务是下一个即将到期的任务。
    • 事件循环会定期检查调度堆,将到期的任务移动到就绪堆。
  • 加入时机:

    • 使用 asyncio.sleep() 或 loop.call_later() 等延迟操作时,协程会被添加到调度堆。

调度时机

协程被添加到调度堆或就绪堆的常见方式包括:

  1. asyncio.sleep**()**:

    • 当协程调用 await asyncio.sleep(delay) 时,事件循环会将协程封装为一个定时任务,并添加到调度堆中,等待 delay 时间后执行。
  2. loop.call_later():

    • 通过 loop.call_later(delay, callback),事件循环会将回调函数封装为定时任务,并添加到调度堆中。
  3. loop.call_at():

    • 通过 loop.call_at(when, callback),事件循环会将回调函数封装为定时任务,并添加到调度堆中,在指定时间 when 执行。
  4. asyncio.create_task():

    • 使用 asyncio.create_task() 创建任务后,任务会在下一次 await 时自动添加到就绪堆。
  5. 协程嵌套调用:

    • 当一个协程 A 中 await 另一个协程 B 时,协程 A 会通过 add_done_callback 方法注册一个唤醒回调。当协程 B 完成时,事件循环会唤醒协程 A 并将其加入就绪堆。

总结

asyncio 的调度机制基于事件循环和协程,通过就绪堆和调度堆管理任务的执行顺序。其核心特点包括:

  • 协作式调度:协程通过 await 主动交出控制权,事件循环根据任务状态调度执行。
  • 高效 I/O 处理:通过 select 系统调用监听 I/O 事件,避免阻塞。
  • 灵活的任务管理:支持定时任务、嵌套协程调用等多种调度方式。

理解 asyncio 的调度机制有助于编写高效、可维护的异步代码,充分利用 Python 的异步编程能力。


参考资料

  • Python 官方文档: https://docs.python.org/3/library/asyncio.html
  • Asyncio 源码: https://github.com/python/cpython/tree/main/Lib/asyncio
Prev
Asyncio