aiofiles 是异步文件 i/o 的正确选择,需 pip install 安装;它基于线程池实现,支持文本/二进制模式、分块读写、并发 gather 及手动 fsync 持久化。

asyncio.open() 不能直接用,得靠 aiofiles
Python 标准库的 asyncio.open() 并不存在——这是初学者最常踩的坑。真正能做异步文件 I/O 的是第三方库 aiofiles,它把 open() 包装成支持 await 的版本,底层仍依赖线程池(loop.run_in_executor)规避 GIL 阻塞。
使用前需安装:pip install aiofiles
- 不推荐自己用
asyncio.to_thread()封装open(),因为aiofiles已处理了文件描述符生命周期、编码、缓冲区等细节 -
aiofiles默认以文本模式打开,若需二进制操作,显式传mode="rb"或mode="wb" - 它不改变文件系统行为:并发写同一文件仍需自行加锁,否则内容会错乱
读大文件时别用 read(),改用 readchunk() 分块
对几百 MB 以上的文件,直接 await f.read() 会一次性加载全部内容到内存,可能触发 MemoryError,也失去流式处理优势。
- 用
async for line in f:按行迭代(适合文本日志类场景) - 更可控的方式是
await f.read(8192)手动分块,每次读固定字节数(如 8KB),配合while True循环 - 注意:分块读取时,
read(n)返回b""表示 EOF,不是None
async with aiofiles.open("log.txt", "r") as f:
while True:
chunk = await f.read(8192)
if not chunk:
break
process(chunk)
并发读多个文件必须用 asyncio.gather(),别用 await 串行
异步 I/O 的收益只在并发等待时体现。如果写成 await read_file("a.txt"); await read_file("b.txt"),实际仍是串行,总耗时 ≈ 两文件读取时间之和。
- 正确做法是构造任务列表后用
await asyncio.gather(*tasks)并发触发 - 若文件数量极大(如上万),避免一次性
gather全部任务,改用asyncio.Semaphore控制并发数(例如限制同时打开 10 个文件句柄) -
aiofiles的close()不是协程,但async with会自动处理,无需手动await f.close()
写文件时 await write() 后不等于数据已落盘
await f.write(data) 只保证数据进入内核缓冲区,断电或崩溃可能导致丢失。要确保持久化,必须显式调用 await f.flush() 和 os.fsync(f.fileno())(后者需用 to_thread 包裹)。
- 普通日志场景可省略
fsync,靠操作系统延迟写入提升吞吐 - 金融/事务类场景必须加
fsync,否则“写成功”只是假象 -
aiofiles不提供fsync协程封装,得自己写:await asyncio.to_thread(os.fsync, f.fileno())
磁盘 I/O 的瓶颈不在 Python 层,而在硬件和系统调度;异步只是让等待时不阻塞事件循环,别指望单次写入变快。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











