因为open()是同步阻塞调用,会卡住asyncio事件循环;aiofiles通过线程池或io_uring实现真正异步文件i/o,避免阻塞。

为什么不能直接用 open() 配合 asyncio
因为 open() 是同步阻塞调用,哪怕你把它放在 async 函数里,也会让整个事件循环卡住——爬虫并发下载几十个页面时,一个文件写操作就能拖慢所有任务。Aiofiles 就是为解决这个而生的:它把文件 I/O 包装成真正的异步操作,底层用线程池或系统级异步接口(如 Linux 的 io_uring)避免阻塞。
aiofiles.open() 的基本写法和常见陷阱
别照搬同步写法。下面这段代码看着对,但实际会出错:
async with aiofiles.open('data.json', 'w') as f:
await f.write(json.dumps(data)) # ❌ 缺少 newline 或 encoding
正确做法必须显式指定编码,且注意换行符处理:
-
aiofiles.open()默认不带encoding,二进制模式下写字符串会报TypeError: a bytes-like object is required - 写 JSON 时建议加
newline='',避免 Windows 下多出空行 - 不要在
with块外 awaitf.write(),否则RuntimeError: file is closed
推荐写法:
async with aiofiles.open('data.json', 'w', encoding='utf-8', newline='') as f:
await f.write(json.dumps(data, ensure_ascii=False, indent=2))
并发写入多个文件时如何避免冲突或覆盖
如果你用同一个文件名反复写(比如按 URL hash 命名),通常没问题;但若多个协程同时写同一路径,结果不可预测——Aiofiles 不做文件锁,它只是把系统调用异步化。
- 写入前用
os.makedirs(..., exist_ok=True)确保目录存在,否则FileNotFoundError - 避免多个协程写同一个文件:用唯一文件名(如
f'data/{hash(url)}.html') - 真要追加内容(如日志),用
'a'模式,但注意不同协程的写入顺序不保证,慎用于结构化数据 - 若需原子写入(防止写到一半被读),先写临时文件,再
os.replace()(Python 3.3+ 支持)
性能对比和何时该换方案
Aiofiles 在多数场景比同步快,但不是万能加速器:
- 小文件(50 路)时,Aiofiles 明显优于
loop.run_in_executor()+ 同步open - 大文件(>1MB)单次写入,Aiofiles 和同步差别不大,甚至略慢(额外封装开销)
- 磁盘 I/O 已是瓶颈时(如机械硬盘),再多协程也无意义,此时应考虑批量写、缓冲区合并或换用 SQLite
- 如果项目已用
asyncpg或aiomysql,优先存数据库而非文件——文件系统本身不支持并发安全写入
真正容易被忽略的是:Aiofiles 不处理文件路径竞争(比如两个协程同时 mkdir 同一目录),这部分仍需你自己加 try/except FileExistsError 或用 pathlib.Path.mkdir(exist_ok=True)。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











