Python aiofiles 异步IO库 作者:马育民 • 2026-08-15 08:15 • 阅读:10002 # 介绍 `aiofiles` 是 Python 生态最主流的**异步磁盘文件库**,Apache‑2.0 协议,专门给 `asyncio` 异步程序做本地文件读写。 ### 原生IO **操作系统本身没有通用的异步磁盘文件API**,Python原生`open()` 文件IO是 **同步阻塞**。直接在协程里调用内置`open()`会卡住事件循环,其他协程得不到调度,并发性能暴跌。 ### 不是真正AIO **aiofiles 不是内核真正AIO,底层是把标准同步文件操作丢到线程池执行,封装成async/await协程接口**,让事件循环不被磁盘IO阻塞。 **提示:**真正的异步IO是要调用操作系统API的,目前没有成熟的库,最流行的是 aiofiles ### 区分 aiofile `aiofiles` ≠ `aiofile`。 `aiofile`是另一个库,可使用 Linux `io_uring`,调用 Linux 真正的异步API `aiofiles` 只用线程池,**跨平台兼容性最好**。 # 安装 要求 Python ≥3.7,最新版本 25.x。 ### pip 安装 ```bash pip install aiofiles ``` ### uv 安装 ```bash uv add aiofiles ``` # 底层原理 1. 调用`aiofiles.open()`返回异步文件包装对象; 2. 所有IO方法(`read/write/seek/flush`等)都是协程; 3. 内部通过 `loop.run_in_executor(ThreadPoolExecutor, 同步文件函数)`,把真正的磁盘IO交给子线程执行; 4. 主线程事件循环继续跑其他协程;线程完成IO后把结果通过await交回协程。 ### 本质 **文件IO本身还是阻塞的,只是阻塞发生在线程池线程,不阻塞事件循环主线程**。磁盘物理IO耗时不会消失,只是不拖累其他任务。 # API说明 接口几乎复刻Python内置`open`,参数完全兼容:`file, mode, buffering, encoding, errors, newline`。 > 注意:文件对象所有IO方法**必须加await**;支持`async with`上下文管理器,自动关闭文件;支持异步迭代按行读取。 ### 常用方法 | 异步方法 | 说明 | |---|---| | `await f.read(size=-1)` | 读取全部/指定字节/字符 | | `await f.readline()` | 读取一行 | | `await f.readlines()` | 读取全部行返回列表 | | `await f.write(data)` | 写入字符串/字节 | | `await f.writelines(lines)` | 批量写入多行 | | `await f.seek(offset)` | 文件指针跳转 | | `await f.tell()` | 获取当前指针位置 | | `await f.flush()` | 刷缓冲区到磁盘 | | `async for line in f:` | 异步逐行迭代,适合大文件 | # 例子 ### 1. 读取文本文件 ```python import asyncio import aiofiles async def read_demo(): async with aiofiles.open("demo.txt", "r", encoding="utf‑8") as f: content = await f.read() print(content) asyncio.run(read_demo()) ``` ### 2. 写入文件 ```python async def write_demo(): async with aiofiles.open("out.txt", "w", encoding="utf‑8") as f: await f.write("第一行\n") await f.write("第二行\n") asyncio.run(write_demo()) ``` ### 3. 追加日志 a模式 ```python async def append_log(): async with aiofiles.open("app.log", "a", encoding="utf‑8") as f: await f.write("2026‑08‑15 日志消息\n") ``` ### 4. 大文件逐行读取(不要一次性read全部) ```python async def read_by_line(): async with aiofiles.open("big.txt", "r", encoding="utf‑8") as f: async for line in f: # 处理每一行 print(line.strip()) ``` ### 5. 二进制读写(下载保存文件场景) ```python async def save_bin(): async with aiofiles.open("test.bin", "wb") as f: await f.write(b"binary data") ``` ### 6. 临时文件支持 ```python from aiofiles import tempfile async def tmp_demo(): async with tempfile.TemporaryFile("wb+") as f: await f.write(b"hello temp") await f.seek(0) data = await f.read() print(data) ``` # 适用场景 1. **FastAPI / aiohttp异步web服务**:读取静态资源、接收上传文件、写访问日志;协程中不能用普通open。 2. **异步爬虫**:大量并发下载,异步保存网页、图片二进制到本地。 3. **异步任务队列**:asyncio大量协程并发读写本地磁盘文件。 4. 大文件分批处理,配合异步迭代。 # 重要限制与注意事项 1. **不是真正内核异步IO**:依赖线程池。当并发文件操作数量极大,线程池会耗尽,出现排队。可自定义executor传入`aiofiles.open`调整线程池。 2. **不适合极高吞吐磁盘IO**:大量磁盘读写瓶颈在磁盘本身,`aiofiles` 无法加速磁盘硬件速度,只是不阻塞事件循环。 3. **不要忘记 `await`**:`f.read()`不加 `await` 得到协程对象,不是真实数据,也不会真正执行IO。 4. 多协程**同时写同一个文件**依然会出现竞态,需要加锁`asyncio.Lock()`,aiofiles本身不提供文件锁。 5. 网络文件系统(NFS/SMB):线程池方案同样生效,但网络文件本身延迟高。 6. 小文件场景收益有限;**如果你的整个程序是同步代码,完全没必要引入aiofiles**。 # 对比 |方案|特点| |---|---| |内置`open()`|同步阻塞,asyncio协程中会卡住事件循环,适合同步代码| |aiofiles|线程池代理,接口贴近原生open,跨平台,生产最常用| |aiofile库|可选io_uring内核异步IO,Linux专用,接口有差异| # 最佳实践 1. 异步程序中所有本地磁盘文件IO统一使用`aiofiles.open`,避免原生`open`。 2. 大文件不要用`await f.read()`一次性全部读入内存,优先`async for`逐行或者分块读取。 3. 多协程写同一个文件,必须加`asyncio.Lock`互斥。 4. 大量并发文件操作,如果线程池打满,可以自定义ThreadPoolExecutor传给loop。 5. 使用`async with`,不要手动调用`await f.close()`,避免文件泄漏。 # 常见坑 ```python # ❌错误,没有await read,拿到协程对象 content = f.read() # ❌错误,协程内使用原生open,阻塞事件循环 async def bad(): with open("a.txt") as f: data = f.read() ``` 原文出处:/show_1GW3rgpNcVpD.html