为何Python异步文件写入比同步写入慢?如何优化异步IO效率?
异步文件写入为何比同步慢?如何正确发挥asyncio优势?
问题描述
我在学习Python的asyncio机制,尝试用aiofiles的异步文件写入提升速度,写了同步和异步两个版本:
同步写入函数:
def sync_writer(): with open("file.txt", "w") as f: for i in range(500000): f.write(f"line#{i}\n")
多协程异步实现:
import asyncio import aiofiles async def write_fun(file, writer_n, n_writers): for i in range(writer_n, 500000, n_writers): await file.write(f"line#{i}\n") async def async_writer(n_writers): async with aiofiles.open("file.txt", "w") as f: await asyncio.gather(*[write_fun(f, i, n_writers) for i in range(n_writers)])
测试后发现,异步版本用10个协程耗时36秒,同步版本仅0.33秒。我以为磁盘IO是密集型任务,异步应该更快,结果相反。请问原因是什么?怎么正确用asyncio和相关库发挥优势?
原因分析
1. 磁盘IO特性不支持单文件多协程并行
异步IO的核心优势是等待IO时不阻塞线程,让线程处理其他任务,但本地磁盘单文件写入无法真正并行:
- 机械硬盘依赖物理磁头寻址,同一时间只能处理一个写入请求;SSD虽并行度更高,但单文件写入受文件系统锁约束,依然是串行执行。
- 同步写入时,操作系统文件缓存会自动将大量小写入合并成大块批量写入磁盘,效率极高;多协程异步写入会把连续写入拆成零散交替请求,破坏缓存批量合并机制,频繁的IO提交和上下文切换开销远大于异步收益。
2. aiofiles的底层实现放大了开销
aiofiles并非原生异步磁盘IO(操作系统无通用异步磁盘IO接口),而是用线程池模拟异步操作:
- 每次
await file.write都会把写入任务提交到线程池,协程挂起等待结果;多协程同时写入同一文件时,线程池内的线程会竞争文件锁,加上频繁线程切换,额外开销巨大。
3. 异步写法存在逻辑问题
多个协程同时写入同一文件,相当于把连续写入拆分成零散的交替小写入,不仅无并行优势,反而因频繁调度和锁竞争拖慢整体速度。
正确使用姿势
1. 选对适用场景
asyncio的异步IO优势主要体现在网络IO密集型任务(比如同时处理上千个HTTP请求、TCP连接),或需要同时等待多个不同IO资源的场景(比如同时读多个文件+请求网络)。本地磁盘单文件写入是典型的不适合异步的场景。
2. 异步处理文件的正确方式
单协程异步写入
如果需要在写入文件的同时处理其他异步任务(比如监听网络请求),用单个协程完成文件写入,效率接近同步写入,还能利用异步非阻塞特性处理其他任务:
import asyncio import aiofiles async def single_coroutine_writer(): async with aiofiles.open("file.txt", "w") as f: for i in range(500000): await f.write(f"line#{i}\n")
多文件并行写入
如果需要同时写入多个不同文件,用异步多协程并行处理,避免等待一个文件写入完成再处理下一个,真正发挥异步优势:
import asyncio import aiofiles async def write_single_file(filename): async with aiofiles.open(filename, "w") as f: for i in range(500000): await f.write(f"line#{i}\n") async def async_multi_file_writer(): files = [f"file_{i}.txt" for i in range(10)] await asyncio.gather(*[write_single_file(f) for f in files])
批量异步写入
把多个小写入合并成一个大字符串,减少await write的调用次数,降低调度开销:
import asyncio import aiofiles async def batch_async_writer(): content = "\n".join([f"line#{i}" for i in range(500000)]) + "\n" async with aiofiles.open("file.txt", "w") as f: await f.write(content)
内容的提问来源于stack exchange,提问作者Dima Bukharev
相关产品推荐
相关产品推荐

