Python aiofiles vs aiofile
Для асинхронной работы с файлами в python на основе asyncio есть две библиотеки aiofiles и aiofile. Какую использовать?
Обе библиотеки не являются истинно асинхронными
Как это ни будет печально для истиного асинхрониста, эти библиотеки просто превращают асинхронные вызовы в потоки. Да да, чем больше откроете файлов, тем больше будет потоков в программе, и хотя это делается незаметно для программиста, по сути получаем просто асинхронный синтаксис, скрывающий многопоточность.
Теоретическое подтверждение
aiofiles - version 0.4.0
эта библиотека напрямую превращает обычные синхронные вызовы в асинхронные с помощью выполнения в потоках, достаточно глянуть исходник:
@delegate_to_executor('close', 'flush', 'isatty', 'read', 'read1', 'readinto'....
этот делегат в конце концов вызывает ._loop.run_in_executor
aiofile - version 1.5.2
эта библиотека с помощью cython использует POSIX AIO, который так написан, что превращает вызовы в потоки.
Вывод
Если POSIX AIO переделают, или подменить POSIX AIO на какой нибудь другой AIO, то у aiofile есть шансы стать истинно асинхронным. Aiofiles без переписывания никогда не станет истинно асинхронным.
Практическое подтверждение
Для подтверждения нам понадобится жесткий диск с медленным доступом, чтобы неспеша насладиться зрелищем потоков в htop. Воспользуемся инструкциями из статьи эмуляция медленного жесткого диска, создадим 3 файла: data1.txt data2.txt data3.txt
aiofiles - run_aiofiles.py
import aiofiles, asyncio
async def read(i):
async with aiofiles.open(f'/mnt/local_share/data{i}.txt') as f:
contents = await f.read()
asyncio.get_event_loop().run_until_complete(asyncio.wait( [read(1),read(2),read(3)] ))
aiofile - run_aiofile.py
import asyncio
from aiofile import AIOFile
async def read(i):
async with AIOFile(f'/mnt/local_share/data{i}.txt', 'r') as f:
contents = await f.read()
asyncio.get_event_loop().run_until_complete(asyncio.wait( [read(1),read(2),read(3)] ))
Использование
В удобстве обе более менее одинаковы, пример записи в файл aiofiles
async def write_async():
txt = ''.join(random.choices(string.ascii_uppercase + string.digits, k=1000))
async with aiofiles.open('/var/bigdata/guru.txt', mode='w') as f:
await f.write(txt)
await f.close()
пример записи в файл aiofile
async def write_async():
txt = ''.join(random.choices(string.ascii_uppercase + string.digits, k=1000))
async with AIOFile('/var/bigdata/guru.txt', 'w') as afp:
await afp.write(txt)
await afp.fsync()
Будем записывать раз в секунду 1000 байт в файл, результат получился интересный:
aiofiles, IO = 4.1 KB/sec | aiofile, IO = 32 KB/sec |
![]() |
![]() |
IO = 4.1 KB/sec вполне ожидаемо, это блок минимального файла, странно что для aiofile получается IO = 32 KB/sec