← Назад к вопросам

Могут ли операции работы с файловой системой быть асинхронными в 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_executoraiofiles
Настоящая асинхронность❌ Нет (потоки)✅ Да
Потребление ресурсовПамять (потоки)Минимум
Простота✅ ВстроенныйТребует библиотеки
СкоростьХорошоЛучше
МасштабируемостьДо ~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 сообществе.

Могут ли операции работы с файловой системой быть асинхронными в asyncio | PrepBro