目 录CONTENT

文章目录

Python中async和await的原理

醉酒的行者
2025-11-16 / 0 评论 / 0 点赞 / 4 阅读 / 0 字

在 Python 中,async 和 await 是 PEP 492(Python 3.5 引入)定义的关键字,用于支持基于协程(coroutine)的异步编程。它们允许在 I/O 密集型任务中实现非阻塞执行,从而在无需多线程或多进程的情况下提升并发性能。

1. 核心概念

  • 协程(Coroutine):一种可暂停和恢复执行的特殊函数,使用 async def 定义。

  • 事件循环(Event Loop):asyncio 模块提供的核心调度机制,负责协作式运行多个协程。

  • 可等待对象(Awaitable):可与 await 配合使用的对象,通常为协程、Task 或 Future。

2 原理

2.1 挂起点(Suspension Point)

  • 当协程执行到 await expr 时:

    • expr 必须返回一个可等待对象。

    • 当前协程立即暂停(suspend)。

    • 控制权交还给事件循环。

2.2 事件循环调度与恢复

  • 事件循环持续监控所有可等待对象的状态(如 I/O 完成、定时器到期)。

  • 一旦 await 后面的对象就绪(done() 为真),事件循环将恢复对应协程的执行。

  • 恢复时,从 await 表达式处继续,获取其返回值。

2.3 协程状态机实现

  • 每个协程在底层被编译为一个生成器对象,包含状态机。

  • await 对应生成器的 yield 点,保存局部变量和执行位置。

  • 事件循环通过 send() / throw() 方法驱动协程前进。

2.4 关键限制与注意事项

  • 只能在 async def 函数内使用 await,否则引发 SyntaxError。

  • 协程不会自动运行,必须通过 asyncio.run()、await 或 create_task() 调度。

  • 阻塞操作会阻碍整个事件循环,应使用异步替代(如 aiohttp 替代 requests)。

2.5. 与线程/进程的对比

特性

异步协程(async/await)

多线程(threading)

并发模型

协作式单线程

抢占式多线程

资源开销

极低(数 KB/协程)

高(数 MB/线程)

适用场景

I/O 密集型(如网络、文件)

CPU 密集型或需真正并行

调试复杂度

较高(需追踪挂起/恢复点)

中等(竞争条件、死锁)

总结

async/await 是“单线程、顺序执行、协作挂起”的编程模型,而非抢占式并发。async 定义可暂停的协程函数,await 实现控制权的协作式让渡。其核心在于事件循环驱动的状态机,通过非阻塞等待实现高效并发。合理使用可显著提升 I/O 密集型应用的吞吐量与响应性。

3 例子

import asyncio
import logging

# 配置日志格式:类似 "2025-10-21 12:12:12,902"
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(name)s- %(levelname)s - %(message)s",
)

logger = logging.getLogger(__name__)


async def fetch_data(delay: int) -> str:
    logger.info(f"开始获取数据,延迟 {delay} 秒...")
    await asyncio.sleep(delay)
    logger.info(f"完成获取,耗时 {delay} 秒")
    return f"数据(延迟 {delay}s)"


async def main():
    task1 = asyncio.create_task(fetch_data(2))
    task2 = asyncio.create_task(fetch_data(1))

    logger.info("main: 执行到 await task1")
    result1 = await task1
    logger.info(f"main: 获得 result1 = {result1}")

    logger.info("main: 执行到 await task2")
    result2 = await task2
    logger.info(f"main: 获得 result2 = {result2}")


# 运行
asyncio.run(main())

4 时序图

0

评论区