asyncio是 Python3.4 + 内置的异步 I/O 框架,核心是通过协程(Coroutine) 实现异步编程,相比多线程 / 多进程,异步编程在 I/O 密集型任务(如网络请求、数据库操作、文件读写)中更轻量、开销更低,且能充分利用 CPU 资源。本文结合可运行示例,帮你从 0 到 1 掌握asyncio的核心逻辑。
- 同步编程:代码按顺序执行,遇到 I/O 操作(如网络请求)时阻塞,等待操作完成后再执行下一行(例:单线程逐一下载 10 个文件,总耗时 = 每个文件下载时间之和);
- 异步编程:代码执行到 I/O 操作时,不阻塞等待,而是 “挂起” 当前任务,去执行其他任务,等 I/O 操作完成后再回来继续执行(例:同时发起 10 个文件下载请求,总耗时≈最慢的那个文件下载时间)。
- 基于单线程(无线程切换开销),通过事件循环调度协程,资源占用远低于多线程;
- 原生支持异步 I/O,无需第三方库即可实现高并发;
- 语法简洁(
async/await关键字),相比回调函数更易维护。
协程是异步编程的核心,需通过async def定义,且必须在事件循环中运行。
单个协程无异步优势,asyncio的核心价值是并发执行多个协程,需通过asyncio.create_task()创建 Task。
import asyncio
import time
任务A开始,延迟2秒
任务B开始,延迟1秒
任务B完成,耗时1.00秒
任务A完成,耗时2.00秒
总耗时:2.00秒
asyncio.create_task():将协程封装为 Task,立即加入事件循环调度(无需等待await);
await task:等待单个 Task 完成,若需等待多个,可使用await asyncio.gather(task1, task2)(更简洁);
asyncio.sleep():异步休眠(非阻塞),替代同步的time.sleep()(后者会阻塞整个事件循环)。
批量管理多个协程 / Task 时,asyncio.gather()比逐个await更简洁,且能收集所有返回值。
import asyncioasync def get_data(num):await asyncio.sleep(1)return f"数据{num}"async def main():
异步编程中若需限制并发数(如避免同时发起 1000 个网络请求压垮服务器),可使用asyncio.Semaphore。
实现协程间的 “等待 - 通知” 机制,类似多线程的threading.Event。
import asyncioasync def wait_event(event):print("协程等待事件触发...")await event.wait()
asyncio最典型的应用是异步网络请求,需结合异步 HTTP 库(如aiohttp),相比同步的requests,并发效率提升 10 倍以上。
import asyncio
import aiohttp
- 问题:在协程中使用
time.sleep()、requests.get()等同步阻塞代码,会阻塞整个事件循环,导致异步失效;
- 解决方案:替换为异步版本(
asyncio.sleep()、aiohttp),若必须使用同步代码,需通过loop.run_in_executor()放到线程池执行:
import asyncio
import time
- 问题:定义协程 / 创建 Task 后,未加
await,导致协程未执行;
- 示例错误代码:
async def task():await asyncio.sleep(1)print("任务执行")asyncio.run(task())
- 解决方案:所有协程 / Task 必须通过
await或asyncio.gather()等待执行。
- 问题:在函数中嵌套定义协程但未调用,或忘记将子协程加入事件循环;
- 解决方案:确保所有协程最终都被
asyncio.run()、await或Task调度。
- 问题:协程中的异常若未捕获,会导致整个事件循环终止;
- 解决方案:在协程内添加
try-except,或通过future.exception()捕获 Task 异常。
- 核心定位:
asyncio适用于 I/O 密集型任务(网络、文件、数据库),CPU 密集型任务仍需用multiprocessing;
- 基础用法:协程用
async def定义,await挂起 I/O 操作,通过asyncio.run()运行,并发用create_task()+gather();
- 避坑核心:避免在协程中使用同步阻塞代码,所有可等待对象必须加
await,异常需手动捕获;
- 实战优选:异步网络请求用
aiohttp,限制并发用Semaphore,批量任务用gather()。