最高效的 IO 并发方案

一、核心基础概念

Python 异步基于 asyncio 标准库,核心要素:

  1. 协程函数 :用 async def 定义,调用后不会立即执行,返回协程对象
  2. await:挂起当前协程,等待 IO 操作完成,期间事件循环可以调度其他协程
  3. 事件循环(EventLoop):异步程序的调度核心,负责轮询 IO、切换协程
  4. 任务(Task):将协程封装为可被事件循环调度的对象,实现并发

关键原则:异步代码中严禁调用同步阻塞接口requests/time.sleep/普通文件读写等),会阻塞整个事件循环。


二、入门:基础语法 & 简单示例

1. 最简协程演示

python 复制代码
import asyncio

# 定义协程函数
async def say_hello(name, delay):
    print(f"开始: {name}")
    # 异步休眠(非阻塞),代替 time.sleep
    await asyncio.sleep(delay)
    print(f"结束: {name}")

# 主入口
async def main():
    # 串行执行
    await say_hello("A", 1)
    await say_hello("B", 1)

# 运行异步程序(Python 3.7+ 标准写法)
if __name__ == "__main__":
    asyncio.run(main())

2. 实现并发:asyncio.create_task + gather

想要并发执行,必须把协程包装为 Task

python 复制代码
import asyncio

async def say_hello(name, delay):
    print(f"开始: {name}")
    await asyncio.sleep(delay)
    print(f"结束: {name}")

async def main():
    # 创建任务,立即交由事件循环调度
    task1 = asyncio.create_task(say_hello("A", 1))
    task2 = asyncio.create_task(say_hello("B", 1))

    # 等待所有任务完成
    await task1
    await task2

# 等价简写:asyncio.gather 批量并发
async def main2():
    tasks = [
        say_hello("A", 1),
        say_hello("B", 1)
    ]
    # 并发执行所有协程,收集返回结果
    results = await asyncio.gather(*tasks)

if __name__ == "__main__":
    asyncio.run(main())
  • 串行总耗时:约 2s
  • 并发总耗时:约 1s

三、主流场景实战(生产常用)

场景1:异步网络请求(爬虫/接口调用)

使用 aiohttp(异步 HTTP 客户端),先安装:

bash 复制代码
pip install aiohttp

基础并发请求

python 复制代码
import asyncio
import aiohttp

# 单个请求协程
async def fetch(session: aiohttp.ClientSession, url: str):
    try:
        async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as resp:
            status = resp.status
            text = await resp.text()
            return f"[{status}] {url} 长度:{len(text)}"
    except Exception as e:
        return f"[失败] {url} 错误:{str(e)}"

async def main():
    urls = [
        "https://httpbin.org/get",
        "https://httpbin.org/delay/1",
        "https://httpbin.org/delay/2"
    ]
    # 复用 Session(官方推荐,减少连接开销)
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        # 并发执行
        res_list = await asyncio.gather(*tasks)
    
    for res in res_list:
        print(res)

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

限制最大并发(防封禁/压垮服务)

使用 Semaphore 信号量 控制并发数:

python 复制代码
import asyncio
import aiohttp

# 限制最大 2 个并发
SEMAPHORE = asyncio.Semaphore(2)

async def fetch(session: aiohttp.ClientSession, url: str):
    async with SEMAPHORE:  # 进入前抢占信号量
        async with session.get(url) as resp:
            return f"{url} -> {resp.status}"

async def main():
    urls = ["https://httpbin.org/get"] * 10
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, u) for u in urls]
        results = await asyncio.gather(*tasks)
        for r in results:
            print(r)

asyncio.run(main())

场景2:异步文件 IO

标准库无原生异步文件,使用 aiofile

bash 复制代码
pip install aiofile
python 复制代码
import asyncio
import aiofile

async def write_file(path, content):
    async with aiofile.async_open(path, "w", encoding="utf-8") as f:
        await f.write(content)

async def read_file(path):
    async with aiofile.async_open(path, "r", encoding="utf-8") as f:
        return await f.read()

async def main():
    # 并发写文件
    write_tasks = [write_file(f"test_{i}.txt", f"内容{i}") for i in range(5)]
    await asyncio.gather(*write_tasks)

    # 并发读文件
    read_tasks = [read_file(f"test_{i}.txt") for i in range(5)]
    contents = await asyncio.gather(*read_tasks)
    print(contents)

asyncio.run(main())

场景3:异步数据库(MySQL 示例)

使用 aiomysql

bash 复制代码
pip install aiomysql
python 复制代码
import asyncio
import aiomysql

async def db_query():
    conn = await aiomysql.connect(
        host="127.0.0.1",
        port=3306,
        user="root",
        password="你的密码",
        db="test_db"
    )
    cur = await conn.cursor()
    await cur.execute("SELECT 1")
    print(await cur.fetchone())
    await cur.close()
    conn.close()

asyncio.run(db_query())

四、异常、超时、重试 工程增强

1. 单个任务设置超时

python 复制代码
async def demo():
    # 超过 2 秒自动抛出超时异常
    try:
        await asyncio.wait_for(asyncio.sleep(3), timeout=2)
    except asyncio.TimeoutError:
        print("任务超时")

asyncio.run(demo())

2. gather 异常处理

  • return_exceptions=True:捕获异常,不中断整体任务
python 复制代码
tasks = [task1, task2, task3]
results = await asyncio.gather(*tasks, return_exceptions=True)

五、关键避坑(高频问题)

  1. 不要混用同步阻塞代码

    禁止在 async def 里用:requeststime.sleep()、普通 open()、同步数据库驱动。

    替换方案:

    • time.sleepasyncio.sleep
    • requestsaiohttp
    • 普通文件 → aiofile
    • 同步 DB → 对应异步驱动
  2. 不要在循环外创建大量 Session/连接

    HTTP、数据库连接复用单例连接池,不要每个任务新建连接。

  3. CPU 密集型不适合异步

    异步单线程,CPU 计算会阻塞事件循环。计算密集型用 multiprocessing 多进程。

  4. 老 Python 版本兼容

    Python3.7+ 推荐 asyncio.run();3.6 及更早需手动管理事件循环。


六、整体使用流程总结

  1. 导入 asyncio,用 async def 定义协程函数
  2. IO 操作全部使用异步专用库 ,搭配 await
  3. 通过 asyncio.create_task() / asyncio.gather() 实现并发
  4. Semaphore 限制并发、wait_for 做超时、捕获异常
  5. 入口统一使用 asyncio.run(main()) 启动事件循环

七、拓展:异步 Web 框架

如果是开发高并发 Web 服务,直接基于异步生态:

  • FastAPI(主流,高性能、易上手)
  • Sanic / Tornado(老牌异步框架)
    底层依然基于 asyncio 事件循环。
相关推荐
Hello:CodeWorld1 小时前
Dify 从入门到实战:部署、模型对接与企业级 AI 应用开发全教程
人工智能·python·架构·ai编程
酣大智1 小时前
BGP选路原则--下一跳IGP Metric小的(8)
网络·华为·路由·bgp
本地化文档1 小时前
black-docs-l10n
python·github·gitcode·sphinx
Dream_ksw1 小时前
Python 基础
开发语言·python
炘爚1 小时前
phase1:基础框架——编译 + MySQL + 登录/注册
linux·c++
齐鲁大虾2 小时前
如何彻底解决从公网HTTP页面请求私有HTTP资源跨域问题
网络·网络协议·http
小蜗子2 小时前
Windows 11 + RTX 5060 + WSL2 Ubuntu + NVIDIA DGL 容器
linux·运维·ubuntu
g3voip2 小时前
洁净室IP电话机是什么?无尘车间语音通信设备的功能与部署要点
网络·网络协议·tcp/ip
清水白石0082 小时前
从打印对象到高质量调试:彻底理解 Python 中 `__repr__` 和 `__str__` 的区别
开发语言·python