← Назад к вопросам
Могут ли операции работы с файловой системой быть асинхронными в asyncio
1.0 Junior🔥 111 комментариев
#Soft Skills#Асинхронность и многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Асинхронная работа с файловой системой в asyncio
Короткий ответ: Нет, стандартный asyncio НЕ поддерживает асинхронные операции с файлами. Но есть несколько способов обойти это ограничение.
Почему файловая система не асинхронна?
import asyncio
async def read_file_wrong():
# ❌ Это СИНХРОННАЯ операция, блокирует весь event loop!
with open("data.txt", "r") as f:
data = f.read() # Блокирует!
return data
async def main():
# При наличии 2+ задач одна блокирует другую
result1 = await read_file_wrong() # Заблокирует event loop
result2 = await read_file_wrong() # Ждёт, пока первая закончится
Причина: Файловая система не предоставляет асинхронные API на уровне операционной системы (в отличие от сетевых операций).
Решение 1: loop.run_in_executor()
Это рекомендуемый способ — запускаем синхронный код в потоке:
import asyncio
async def read_file_async():
loop = asyncio.get_event_loop()
# Запускаем синхронную функцию в потоке
data = await loop.run_in_executor(
None, # None = ThreadPoolExecutor по умолчанию
read_file,
"data.txt"
)
return data
def read_file(filename):
"""Обычная синхронная функция"""
with open(filename, "r") as f:
return f.read()
async def main():
# Теперь обе операции работают параллельно!
task1 = read_file_async() # В потоке 1
task2 = read_file_async() # В потоке 2
result1, result2 = await asyncio.gather(task1, task2)
print(f"File 1: {len(result1)} bytes")
print(f"File 2: {len(result2)} bytes")
asyncio.run(main())
Решение 2: aiofiles — асинхронный API
Возможно, самое удобное решение для работы с файлами:
pip install aiofiles
import asyncio
import aiofiles
async def read_file_with_aiofiles(filename):
# Настоящее асинхронное чтение!
async with aiofiles.open(filename, mode="r") as f:
data = await f.read()
return data
async def main():
# Обе операции параллельны
result1 = await read_file_with_aiofiles("file1.txt")
result2 = await read_file_with_aiofiles("file2.txt")
print(f"File 1: {len(result1)} chars")
print(f"File 2: {len(result2)} chars")
asyncio.run(main())
Сравнение подходов
Синхронный код (блокирует)
import time
def sync_read_multiple():
start = time.time()
for i in range(5):
with open(f"file_{i}.txt", "r") as f:
data = f.read()
return time.time() - start
print(f"Sync: {sync_read_multiple():.2f}s") # ~5s (последовательно)
loop.run_in_executor()
import asyncio
import time
async def async_read_with_executor():
loop = asyncio.get_event_loop()
start = time.time()
tasks = []
for i in range(5):
task = loop.run_in_executor(
None,
read_file,
f"file_{i}.txt"
)
tasks.append(task)
await asyncio.gather(*tasks)
return time.time() - start
def read_file(filename):
with open(filename, "r") as f:
return f.read()
print(f"Executor: {asyncio.run(async_read_with_executor()):.2f}s") # ~1s
aiofiles (лучшее решение)
import asyncio
import aiofiles
import time
async def async_read_with_aiofiles():
start = time.time()
async def read_one(i):
async with aiofiles.open(f"file_{i}.txt") as f:
return await f.read()
tasks = [read_one(i) for i in range(5)]
await asyncio.gather(*tasks)
return time.time() - start
print(f"aiofiles: {asyncio.run(async_read_with_aiofiles()):.2f}s") # ~1s
Практические примеры
Чтение JSON файла
import asyncio
import aiofiles
import json
async def read_json_async(filename):
async with aiofiles.open(filename, mode="r") as f:
content = await f.read()
return json.loads(content)
async def main():
data = await read_json_async("config.json")
print(data)
asyncio.run(main())
Запись в файл асинхронно
import asyncio
import aiofiles
async def write_json_async(filename, data):
async with aiofiles.open(filename, mode="w") as f:
await f.write(json.dumps(data, indent=2))
async def main():
data = {"name": "test", "value": 42}
await write_json_async("output.json", data)
print("Written")
asyncio.run(main())
Обработка множественных файлов
import asyncio
import aiofiles
import glob
async def process_files(pattern):
files = glob.glob(pattern)
async def process_one(filepath):
async with aiofiles.open(filepath) as f:
content = await f.read()
return filepath, len(content)
results = await asyncio.gather(*[
process_one(f) for f in files
])
return results
async def main():
results = await process_files("*.txt")
for filepath, size in results:
print(f"{filepath}: {size} bytes")
asyncio.run(main())
run_in_executor vs aiofiles
| Аспект | run_in_executor | aiofiles |
|---|---|---|
| Настоящая асинхронность | ❌ Нет (потоки) | ✅ Да |
| Потребление ресурсов | Память (потоки) | Минимум |
| Простота | ✅ Встроенный | Требует библиотеки |
| Скорость | Хорошо | Лучше |
| Масштабируемость | До ~100 файлов | 1000+ файлов |
| Использование | Любой синхронный код | Только файлы |
Важные моменты
1. Не блокируй event loop
# ❌ ПЛОХО: блокирует event loop
async def bad():
data = open("large.txt").read() # Может занять секунды!
return data
# ✅ ХОРОШО: не блокирует
async def good():
loop = asyncio.get_event_loop()
data = await loop.run_in_executor(None, open_file)
return data
2. Размер пула потоков
import asyncio
from concurrent.futures import ThreadPoolExecutor
# По умолчанию ThreadPoolExecutor использует min(32, os.cpu_count() + 4)
# Для I/O можно увеличить
executor = ThreadPoolExecutor(max_workers=10)
loop = asyncio.get_event_loop()
loop.set_default_executor(executor)
3. Асинхронная работа с БД
# aiosqlite для SQLite
import aiosqlite
async def query_db():
async with aiosqlite.connect("db.sqlite") as db:
async with db.execute("SELECT * FROM users") as cursor:
rows = await cursor.fetchall()
return rows
Резюме
Могут ли файловые операции быть асинхронными?
- ✅ Формально да, но нужна дополнительная библиотека (
aiofiles) - ❌ Стандартный asyncio не поддерживает асинхронные файлы
- ✅ loop.run_in_executor() — встроённое решение (использует потоки)
- ✅ aiofiles — лучшее решение для файлов
Рекомендация: Используй aiofiles для асинхронной работы с файлами, это стандарт в Python async сообществе.